SpringBoot嵌套事务中内层方法隔离级别是否会被外层事务覆盖?
Spring Boot JDBC 嵌套事务问题解答
业务场景代码如下:
@Named @Transactional(isolation=Isolation.READ_COMMITTED) // READ_COMMITTED < SERIALIZABLE class MyService { void do() { myRepository.add(entity); ... } } @Repository class MyRepository { @Transactional(propagation=Propagation.REQUIRED, isolation=Isolation.SERIALIZABLE) void add(entity) {...} }
问题1:MyService.do()的事务是否会覆盖MyRepository.add的隔离级别
当前配置下,外层的READ_COMMITTED隔离级别确实会覆盖add方法配置的SERIALIZABLE,导致隔离级别严格程度降低。
原因:Spring声明式事务中,Propagation.REQUIRED的逻辑是如果当前线程已经存在事务,就直接加入该事务,不会新开启独立事务,此时整个事务的所有属性(隔离级别、超时、只读等)都以最先开启的外层事务配置为准,内层方法的事务属性会被直接忽略。当前MyService已经启动了一个READ_COMMITTED的事务,add方法加入这个事务后,实际生效的隔离级别就是READ_COMMITTED,达不到最高锁等级的要求。
如果要保证add方法的隔离级别始终为SERIALIZABLE不被覆盖,需要把add方法的传播属性修改为Propagation.REQUIRES_NEW,这样执行add方法时会先挂起外层的READ_COMMITTED事务,新开启一个隔离级别为SERIALIZABLE的独立事务,add方法执行完成后再恢复外层事务,即可满足需求。
问题2:处理嵌套事务的注意事项
- 明确传播属性的适用场景:
REQUIRED是默认配置,适合所有操作需要同成功同失败的联动场景;REQUIRES_NEW适合内层操作需要独立提交/回滚、或者需要独立隔离级别的场景;NESTED是基于JDBC保存点实现的嵌套事务,外层事务回滚会同步回滚内层操作,但内层异常可以仅回滚到保存点不影响外层逻辑,三者不要混用错。 - 隔离级别生效规则:如果内层事务是加入已有事务的场景,自定义隔离级别不会生效,只有新开启的独立事务才会使用自己配置的隔离级别。
- 避免自调用导致事务失效:Spring的声明式事务基于AOP代理实现,同一个类内部的方法调用不会触发代理逻辑,内层方法的事务配置会直接失效,需要调用代理对象的方法才能让事务配置生效。
- 匹配异常回滚规则:Spring默认只对RuntimeException和Error触发事务回滚,如果业务会抛出受检异常,需要通过
@Transactional(rollbackFor = 异常类.class)显式指定要触发回滚的异常类型。 - 注意数据库的能力限制:比如
NESTED传播级别依赖底层JDBC驱动支持保存点,部分数据库不支持该特性,使用前要先确认数据库的兼容能力。 - 跨事务的数据可见性:如果使用
REQUIRES_NEW开启独立内层事务,外层事务未提交的数据在内层事务中是不可见的,要注意业务逻辑上的数据一致性问题。
内容的提问来源于stack exchange,提问作者ses
相关产品推荐
相关产品推荐

