java - 我把 noRollbackFor 放在哪里有关系吗?

标签 java spring spring-transactions

在 Spring ,如果我有:

ServiceA.serviceA() -> ServiceB.serviceB() -> ServiceC.serviceC() ->ServiceD.serviceD()

ServiceD.serviceD() 可以抛出运行时异常:MyRuntimeException,它会传播回 ServiceA.serviceA catch block 。我将 @Transactional(noRollbackFor=[MyRuntimeException.class]) 放在哪个服务上重要吗?

把它放在任何服务上有什么区别吗?

注意:我所有的服务都被标记为@Transactional

最佳答案

由于您没有给出精确的信息,我假设您使用的是 PROPAGATION_REQUIRED 的默认传播。在这种情况下,4 个服务将使用相同的事务,如果三个内部服务中的任何一个由于异常而将事务标记为只读,外部服务将收到 UnexpectedRollbackException 通知它请求的提交实际上导致了回滚。来自 Spring 引用手册:但是,在内部事务范围设置仅回滚标记的情况下,外部事务尚未决定回滚本身,因此回滚(由内部事务范围静默触发)是意外。此时会抛出相应的 UnexpectedRollbackException。这是预期的行为,因此事务的调用者永远不会被误导以为提交已执行,但实际上并未执行。因此,如果内部事务(外部调用者不知道)默默地将事务标记为仅回滚,则外部调用者仍会调用提交。外部调用者需要接收 UnexpectedRollbackException 以清楚地指示执行了回滚。。而如果外层事务因为异常决定回滚事务,则事务显然会被回滚。

因此,如果没有任何服务捕获异常,并且如果您使用 PROPAGATION_REQUIRED 的传播,至少四个涉及的方法必须使用 @Transactional(noRollbackFor=[MyRuntimeException .class]).

使用 noRollbackFor=[MyRuntimeException.class] 的替代方法是在 ServiceD 的适当方法中捕获异常。在那种情况下,异常将永远不会爬上堆栈,并且任何事务代理都不会知道它发生了。提交通常会在事务结束时发生。

根据评论编辑:

如果您想进一步控制异常管理,您可以尝试复制方法:一种事务性方法,它在您的服务类中调用非事务性方法。如果您不想在链中使用另一个事务代理,则可以调用非事务代理。但这只有在这个用例(一个服务类调用另一个具有特殊异常要求的服务类)异常时才有意义。

作为替代方案,您可以在其他服务类上注入(inject)实现,而不是注入(inject)事务代理 (@Autowired private ServiceBImpl serviceB;)。由于您已经在外层获得了一个事务,因此所有 DAO 操作都应该没问题,并且由于在外层只有一个事务代理,您有一个用于异常管理的单一控制点。注入(inject)类而不是接口(interface)是相当不常见的,您应该用红色闪烁字体记录原因 :-),但它应该满足您的要求。

关于java - 我把 noRollbackFor 放在哪里有关系吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25489547/

相关文章:

java - 不在事务中时回滚 Spring JDBC 操作

java - 如何使用java将提问者文档转换为xml格式

java - 电子邮件的附件?

java - 为什么完整的 Arraylist 没有通过第二个 for 循环打印出来?

java - 为什么调用 get(int) 后 java.util.calendar 对象的值会发生变化

java - 忘记密码功能 Spring(密码找回)

java - Spring Boot Security 中 OAuth2Client 中的 SSO

java - Hibernate 和 Spring DataSourceTransactionManager

mysql - spring transaction 是否仅在进入服务方法中起作用?

java - Logback - 根据大小滚动文件