Spring Data JPA + Hibernate 将方法标记为事务
Posted
技术标签:
【中文标题】Spring Data JPA + Hibernate 将方法标记为事务【英文标题】:Spring Data JPA + Hibernate Marking methods as Transactional 【发布时间】:2021-03-10 02:41:31 【问题描述】:我正在为我的应用程序使用 Spring data JPA 的存储库。目前我正在使用 spring data jpa 存储库默认提供的基本 CRUD 操作,对于复杂的连接查询,我正在编写自定义 JPQL 查询,如下所示:
public interface ContinentRepository extends JpaRepository<Continent, Long>
@Query(value = "SELECT u FROM Continent u JOIN FETCH ... WHERE u.id = ?1")
public List<Continent> findContinent(Long id);
在我的服务类中,我正在自动装配这个存储库并执行数据库操作。
@Service
public class MyService
@Autowired
public ContinentRepository cr;
public void read()
var result1 = cr.findContinent(1);
var result2 = cr.findContinent(2);
@Transactional
public void write()
var c = new Continent();
// setters
c = cr.save(c);
目前我只是将write()
标记为org.springframework.transaction.annotation.Transactional
。
-
我是否也应该用
Transactional(readOnly = true)
标记read()
方法?因为它只执行读取操作。
我是否也应该将findContinent(Long id)
标记为事务性(readOnly = true)?我读到所有默认方法都标记为 Transactional https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#transactions
在存储库接口中,我应该在方法级别还是在接口级别标记Transactional注解。 (另外,我怀疑大多数自定义方法都是只读的)
在 Service 层和 Repository 层都有 @Transactional 好不好?
【问题讨论】:
【参考方案1】:我还应该用 Transactional(readOnly = true) 标记 read() 方法吗?因为它只执行读取操作。
不是很必要,但它可能会根据此博客 https://vladmihalcea.com/spring-read-only-transaction-hibernate-optimization/ 对缓存内存消耗进行一些优化
我是否也应该将 findContinent(Long id) 标记为 Transactional(readOnly = true)?我读到所有默认方法为 标记为事务性 https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#transactions
与第一种情况相同的答案
在存储库接口中,我应该在方法级别还是在接口级别标记Transactional注解。 (另外,我 怀疑大多数自定义方法都是只读的)
一般我是加到方法层的,因为我对回滚等参数有更多的控制权
在 Service 层和 Repository 层都有 @Transactional 好不好?
我建议您使用服务级别,因为在那里您可以更新许多表(因此您可以使用许多存储库),并且您希望您的整个更新都是事务性的。
【讨论】:
@Transactional(readOnly = true)
是多余的并不是严格意义上的。这取决于隔离级别。虽然对于READ COMMITED
确实是多余的,但对于REPEATABLE READ
和更高的隔离级别,其行为将不同,其中不会立即释放读锁
如果我用 Transactional 注释 Service 类,并用 Transactional 注释该服务类内的方法(但与类级别 Transactional 注释相比具有不同的属性),那么将应用哪个注释。事务注解是否支持方法级别的覆盖属性?
另外,当一个服务方法调用另一个服务的方法并且都具有REQUIRED
的传播但其他属性不同时。例如:服务A中带有@Transactional
的方法调用服务B中带有@Transactional(readOnly=true)
的方法,那么在这种情况下是否创建了一个新事务?
这篇文章描述了注释应用的优先顺序baeldung.com/…但是我在spring docs中找不到任何这样的东西。
@JavaLearner 如果您使用@Transactional
注释服务并在内部调用使用@Transactional
注释的存储库方法,那么您需要为回购事务注释指定传播REQUIRED
,以便加入已经存在的交易并且不创建另一个交易。但是,您应该检查您的数据库是否支持这一点(它应该)。一般来说,我不会这样做,因为我只在服务中调用存储库,并且存储库类是包保护的,因此不会被注入到位于其包之外的类中。【参考方案2】:
我还应该用 Transactional(readOnly = true) 标记 read() 方法吗?因为它只执行读取操作。
是的。这是一个很好的做法,您会立即知道此方法不会更改数据库中的任何内容,更重要的是,您将使用此服务方法禁用数据库中的一些无意更改。如果此方法尝试对 DB 进行任何更新,则会抛出异常并调用回滚。
我是否也应该将 findContinent(Long id) 标记为 Transactional(readOnly = true)?我读到所有默认方法为 标记为事务性 https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#transactions
取决于您如何使用它。如果您总是从@Sevice 调用它,那么不会。我认为这是我所知道的常见用例。
在存储库界面中,我是否应该标记 Transactional 方法级别或接口级别的注释。 (另外,我 怀疑大多数自定义方法都是只读的)
同 2。
在 Service 和 Repository 中都有 @Transactional 是否很好 层?
同 2。
【讨论】:
关于#1,它取决于提供者docs.spring.io/spring-framework/docs/current/javadoc-api/org/… 如果你使用 Spring Data 和 Hibernate,这是真的。我是从那次经验中谈起的,它是最常用的堆栈。对我来说,如果实现不解释只读是可以接受的。当您在更大的团队中工作时,这一点非常重要。以上是关于Spring Data JPA + Hibernate 将方法标记为事务的主要内容,如果未能解决你的问题,请参考以下文章
spring-data-jpa 和 spring-boot-starter-data-jpa 的区别
spring-data详解之spring-data-jpa:简单三步快速上手spring-data-jpa开发