如何在 Spring Data 中使用@Transactional?

新手上路,请多包涵

我刚刚开始从事 Spring-data、Hibernate、MySQL、JPA 项目。我切换到 spring-data 这样我就不必担心手动创建查询。

我注意到使用 spring-data 时不需要使用 @Transactional 因为我也尝试了没有注释的查询。

是否有我应该/不应该使用 @Transactional 注释的特定原因?

作品:

 @Transactional
public List listStudentsBySchool(long id) {
    return repository.findByClasses_School_Id(id);
}

也有效:

 public List listStudentsBySchool(long id) {
    return repository.findByClasses_School_Id(id);
}

原文由 Byron Voorbach 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 375
2 个回答

你的问题实际上是关于什么的? @Repository 注释或 @Transactional 的用法。

@Repository 根本不需要,因为您声明的接口将由 Spring Data 基础结构创建并激活异常转换的代理支持。因此,在 Spring Data 存储库接口上使用此注释根本没有任何效果。

@Transactional - 对于 JPA 模块,我们在支持代理的实现类上有此注释( SimpleJpaRepository )。这有两个原因:首先,持久化和删除对象需要 JPA 中的事务。因此,我们需要确保事务正在运行,我们通过使用 @Transactional 注释的方法来做到这一点。

Reading methods like findAll() and findOne(…) are using @Transactional(readOnly = true) which is not strictly necessary but triggers a few optimizations in the transaction infrastructure (setting the FlushModeMANUAL 让持久性提供程序在关闭 EntityManager 时可能跳过脏检查)。除此之外,该标志还在 JDBC 连接上设置,这会导致在该级别上进一步优化。

根据您使用的数据库,它可以忽略表锁甚至拒绝您可能意外触发的写操作。因此,我们建议使用 @Transactional(readOnly = true) 作为查询方法,您也可以轻松实现将注释添加到存储库界面。确保将普通的 @Transactional 添加到您可能已在该接口中声明或重新修饰的操作方法中。

原文由 Oliver Drotbohm 发布,翻译遵循 CC BY-SA 3.0 许可协议

在您的示例中,它取决于您的存储库是否有 @Transactional

如果是,那么服务,(按原样)在你的情况下 - 不应该使用 @Transactional (因为没有必要使用它)。您可以添加 @Transactional 稍后,如果您计划向您的服务添加更多逻辑来处理另一个表/存储库 - 那么拥有它是有意义的。

如果没有 - 那么您的服务应该使用 @Transactional 如果您想确保您没有隔离问题,例如您没有阅读尚未通勤的内容。

如果谈论一般的存储库(作为 crud 集合接口):

  1. 我会说:不,你不应该使用@Transactional

为什么不:如果我们 认为 存储库在业务上下文之外,并且它不应该知道传播或隔离(锁定级别)。它无法猜测它可能涉及到哪个事务上下文。

存储库是“无业务”的(如果您相信的话)

比如说,你有一个存储库:

 class MyRepository
   void add(entity) {...}
   void findByName(name) {...}

并且有一个业务逻辑,比如 MyService

  class MyService() {

   @Transactional(propagation=Propagation.REQUIRED, isolation=Isolation.SERIALIZABLE)
   void doIt() {
      var entity = myRepository.findByName("some-name");
      if(record.field.equal("expected")) {
        ...
        myRepository.add(newEntity)
      }
   }

 }

即在这种情况下: MyService 决定它想要将存储库纳入什么。

在这种情况下,使用 propagation=“Required” 将确保两种存储库方法 - findByName()add() 将涉及单个事务,并且 isolation=“Serializable” 将确保没有人可以干扰那个。它将锁定涉及 get() 和 add() 的那个表。

但是其他一些服务可能想以不同的方式使用 MyRepository,根本不涉及任何事务,比如它使用 findByName() 方法,对任何限制读取它此时可以找到的任何内容不感兴趣。

  1. 我会说是,如果您将您的存储库视为返回始终有效实体(无脏读)等的存储库(避免用户错误使用它)。也就是说,您的存储库应该处理隔离问题(并发性和数据一致性),例如:

我们希望(存储库)确保当我们 add(newEntity) 它首先检查是否已经存在具有相同名称的实体,如果是 - 插入,全部在一个锁定工作单元中。 (与我们在上面的服务级别所做的相同,但不是我们将此责任转移到存储库)

比如说,不能有 2 个任务具有相同的名称“进行中”状态(业务规则)

  class TaskRepository
   @Transactional(propagation=Propagation.REQUIRED,
   isolation=Isolation.SERIALIZABLE)
   void add(entity) {
      var name = entity.getName()
      var found = this.findFirstByName(name);
      if(found == null || found.getStatus().equal("in-progress"))
      {
        .. do insert
      }
   }
   @Transactional
   void findFirstByName(name) {...}

第二个更像是 DDD 风格的存储库。


如果出现以下情况,我想还有更多要涵盖的内容:

   class Service {
    @Transactional(isolation=.., propagation=...) // where .. are different from what is defined in taskRepository()
    void doStuff() {
      taskRepository.add(task);
    }
  }

原文由 ses 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题