95

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

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

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

作品:

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

也有效:

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

提前致谢!

4

6 回答 6

153

你的问题到底是什么?注释@Repository@Transactional.

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

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

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

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

于 2012-05-05T22:44:17.983 回答
4

我认为这个问题有点宽泛,不能在数据访问层的注释上减少。我们需要考虑应用程序的整个堆栈、我们想要应用的事务策略等等。Mark Richards 在 IBM developerworks 站点上有一组非常全面的关于此主题的文章。您可以在这里找到第一个:https ://developer.ibm.com/articles/j-ts1/

最好的祝福

于 2012-05-06T11:43:26.607 回答
1

在您的示例中,这取决于您的存储库是否有@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" 将确保 BOTH 存储库方法 -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) {...}

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


我想如果出现以下情况,还有更多内容需要介绍:

  class Service {
    @Transactional(isolation=.., propagation=...) // where .. are different from what is defined in taskRepository()
    void doStuff() {
      taskRepository.add(task);
    }
  }
于 2021-11-25T22:24:26.203 回答
0

你应该使用@Repository注释

这是因为@Repository用于将未经检查的 SQL 异常转换为 Spring Excpetion,您应该处理的唯一异常是DataAccessException

于 2012-05-01T07:39:06.657 回答
0

我们还使用@Transactional 注释来锁定记录,以便另一个线程/请求不会更改读取。

于 2020-01-15T14:00:02.107 回答
0

@Transactional当我们同时创建/更新一个实体时,我们使用注释。如果@Transactional抛出异常的方法,注释有助于回滚先前的插入。

于 2021-11-19T13:16:54.357 回答