【问题标题】:Spring Data JPA + Hibernate Marking methods as TransactionalSpring Data JPA + Hibernate 将方法标记为事务
【发布时间】: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

  1. 我是否也应该用Transactional(readOnly = true) 标记read() 方法?因为它只执行读取操作。
  2. 我是否也应该将findContinent(Long id) 标记为事务性(readOnly = true)?我读到所有默认方法都标记为 Transactional https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#transactions
  3. 在存储库接口中,我应该在方法级别还是在接口级别标记Transactional注解。 (另外,我怀疑大多数自定义方法都是只读的)
  4. 在 Service 层和 Repository 层都有 @Transactional 好不好?

【问题讨论】:

    标签: java hibernate jpa spring-data-jpa


    【解决方案1】:
    1. 我还应该用 Transactional(readOnly = true) 标记 read() 方法吗?因为它只执行读取操作。

    不是很必要,但它可能会根据此博客 https://vladmihalcea.com/spring-read-only-transaction-hibernate-optimization/ 对缓存内存消耗进行一些优化

    1. 我是否也应该将 findContinent(Long id) 标记为 Transactional(readOnly = true)?我读到所有默认方法为 标记为事务性 https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#transactions

    与第一种情况相同的答案

    1. 在存储库接口中,我应该在方法级别还是在接口级别标记Transactional注解。 (另外,我 怀疑大多数自定义方法都是只读的)

    一般我是加到方法层的,因为我对回滚等参数有更多的控制权

    1. 在 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】:
    1. 我还应该用 Transactional(readOnly = true) 标记 read() 方法吗?因为它只执行读取操作。

    是的。这是一个很好的做法,您会立即知道此方法不会更改数据库中的任何内容,更重要的是,您将使用此服务方法禁用数据库中的一些无意更改。如果此方法尝试对 DB 进行任何更新,则会抛出异常并调用回滚。

    1. 我是否也应该将 findContinent(Long id) 标记为 Transactional(readOnly = true)?我读到所有默认方法为 标记为事务性 https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#transactions

    取决于您如何使用它。如果您总是从@Sevice 调用它,那么不会。我认为这是我所知道的常见用例。

    1. 在存储库界面中,我是否应该标记 Transactional 方法级别或接口级别的注释。 (另外,我 怀疑大多数自定义方法都是只读的)

    同 2。

    1. 在 Service 和 Repository 中都有 @Transactional 是否很好 层?

    同 2。

    【讨论】:

    • 如果你使用 Spring Data 和 Hibernate,这是真的。我是从那次经验中谈起的,它是最常用的堆栈。对我来说,如果实现不解释只读是可以接受的。当您在更大的团队中工作时,这一点非常重要。
    猜你喜欢
    • 2016-08-03
    • 2018-02-04
    • 2011-08-06
    • 2017-07-17
    • 2019-07-20
    • 2016-06-26
    • 2016-10-15
    • 1970-01-01
    • 2019-06-15
    相关资源
    最近更新 更多