如何将 @Transactional 与 Spring Data 一起使用?
Posted
技术标签:
【中文标题】如何将 @Transactional 与 Spring Data 一起使用?【英文标题】:How to use @Transactional with Spring Data? 【发布时间】:2012-05-10 19:29:39 【问题描述】:我刚开始从事 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);
提前致谢!
【问题讨论】:
【参考方案1】:你应该使用@Repository
注解
这是因为 @Repository
用于将未经检查的 SQL 异常转换为 Spring Excpetion,而您应该处理的唯一异常是 DataAccessException
【讨论】:
这在使用 Spring 时通常是正确的,但由于 Spring Data 存储库已经由 Spring 代理支持 - 使用 @Repository 没有任何区别。【参考方案2】:您的问题实际上是关于什么的? @Repository
注解或@Transactional
的用法。
@Repository
根本不需要,因为您声明的接口将由 Spring Data 基础架构创建并激活异常转换的代理支持。所以在 Spring Data 存储库接口上使用这个注解根本没有任何效果。
@Transactional
- 对于 JPA 模块,我们在支持代理 (SimpleJpaRepository
) 的实现类上有这个注释。这有两个原因:首先,持久化和删除对象需要 JPA 中的事务。因此,我们需要确保事务正在运行,我们通过使用@Transactional
注释方法来做到这一点。
像findAll()
和findOne(…)
这样的读取方法正在使用@Transactional(readOnly = true)
,这不是绝对必要的,但会触发事务基础结构中的一些优化(将FlushMode
设置为MANUAL
以让持久性提供程序可能跳过脏检查当关闭EntityManager
)。除此之外,该标志也在 JDBC 连接上设置,这会导致该级别的进一步优化。
根据您使用的数据库,它可以忽略表锁,甚至拒绝您可能意外触发的写入操作。因此,我们建议使用@Transactional(readOnly = true)
作为查询方法,您可以轻松地实现将该注释添加到您的存储库接口。确保将一个普通的@Transactional
添加到您可能在该接口中声明或重新装饰的操作方法中。
【讨论】:
简而言之:我应该在添加/编辑/删除查询上使用@Transactional,在我所有 DAO 方法的选择查询上使用@Transaction(readOnly = true)? 没错。最简单的方法是在接口上使用@Transactional(readOnly = true)
(因为它通常包含大部分查找器方法)并使用普通@Transactional
覆盖每个修改查询方法的此设置。这实际上是在SimpleJpaRepositoy
中完成的方式。
@Oliver 感谢您的全面解释..但是在通过其他链接 [transaction-pit-falls] ibm.com/developerworks/java/library/j-ts1/index.html#listing8> 时。上面写着 "底线是,当你使用基于 ORM 的框架时,只读标志是毫无用处的,在大多数情况下会被忽略。但如果你仍然坚持使用它,请始终设置传播模式到支持“..读完后我不确定我是否应该单独使用(readOnly = true)..它是否应该始终与传播模式一起使用作为支持。
本文这部分内容接近一切都是错误的。通过表明您没有在编写,JDBC 驱动可以(将)提高数据库交互的性能。它还可以检测和拒绝意外发出的写入。最重要的是,Spring 在只读模式下禁用 JPA/Hibernate 刷新,如果您读取大型对象图,这会极大地影响性能,因为提供者不需要对其执行脏检查。虽然该标志可能对交易本身没有太大影响,但到目前为止还不是所有要考虑的事情。
@MartinAndersson 注意,任何与数据库的对话都在事务中运行。在这里查看很好的解释和更多要了解的内容:***.com/questions/13539213/…【参考方案3】:
我认为这个问题有点宽泛,不能在数据访问层的注释上减少。我们需要考虑应用程序的整个堆栈、我们想要应用的事务策略等等。 Mark Richards 在 IBM developerworks 站点上有一组非常全面的关于此主题的文章。你可以在这里找到第一个:https://developer.ibm.com/articles/j-ts1/
最好的问候
【讨论】:
链接已失效。【参考方案4】:我们还使用@Transactional 注释来锁定记录,这样另一个线程/请求就不会改变读取。
【讨论】:
【参考方案5】:当我们同时创建/更新一个实体时,我们使用@Transactional
注解。如果有@Transactional
的方法抛出异常,注解有助于回滚之前的插入。
【讨论】:
【参考方案6】:在您的示例中,这取决于您的存储库是否有 @Transactional
。
如果是,那么在您的情况下(按原样)服务 - 不应该使用@Transactional
(因为没有必要使用它)。如果您打算在处理其他表/存储库的服务中添加更多逻辑,您可以稍后添加@Transactional
- 然后会有一点。
如果不是,那么您的服务应该使用@Transactional
,以确保您没有隔离问题,例如您没有阅读尚未通勤的内容。
--
如果谈论一般的存储库(作为 crud 收集接口):
-
我会说:不,你不应该使用@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()
方法,对读取任何它现在可以找到的任何内容不感兴趣。
-
我会说是的,如果您将存储库视为始终返回有效实体(无脏读)等的存储库(避免用户错误地使用它)。 IE。您的存储库应处理隔离问题(并发性和数据一致性),例如:
我们希望(存储库)确保当我们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);
【讨论】:
以上是关于如何将 @Transactional 与 Spring Data 一起使用?的主要内容,如果未能解决你的问题,请参考以下文章
SpringBoot整合Shiro 涉及跨域和@Cacheable缓存/@Transactional事务注解失效问题
为啥将@Transactional 与@Service 一起使用而不是与@Controller 一起使用
为啥将@Transactional 与@Service 一起使用而不是与@Controller 一起使用