如何将@Transactional与Spring Data结合使用?

2022-08-31 10:19:31

我刚刚开始研究Spring-data,Hibernate,MySQL,JPA项目。我切换到了 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);
}

答案 1

你的问题到底是关于什么的?注释 或 的用法。@Repository@Transactional

@Repository根本不需要,因为您声明的接口将由Spring Data基础架构创建的代理支持,并激活异常转换。因此,在Spring Data存储库界面上使用此注释根本不会产生任何影响。

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

读取像 和 正在使用的方法,这不是绝对必要的,但会在事务基础结构中触发一些优化(将 设置为 让持久性提供程序在关闭 时可能跳过脏检查)。除此之外,在JDBC连接上也设置了标志,这会导致该级别的进一步优化。findAll()findOne(…)@Transactional(readOnly = true)FlushModeMANUALEntityManager

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


答案 2

在您的示例中,这取决于您的存储库是否有。@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”将确保两种存储库方法 - 并且将参与单个事务,并且 isolation=“Serializable”将确保没有人可以干扰它。它将为涉及 get() & add() 的表保持锁定。findByName()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) {...}

2nd更像是DDD风格的存储库。


我想如果:

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

推荐