java事务嵌套问题核心在于传播行为配置:默认required导致异常连坐回滚;requires_new新建独立事务,适合弱一致性操作;nested利用保存点实现局部回滚;同类方法调用需通过代理避免事务失效。

Java 中事务嵌套出问题,核心不是“能不能嵌套”,而是传播行为没配对——外层方法调用内层方法时,Spring 怎么决定“用同一个事务”还是“开新事务”或“设保存点”,直接决定异常是否连坐、数据是否全丢。
默认 REQUIRED 容易连坐回滚
两个方法都加 @Transactional,没指定 propagation,默认都是 REQUIRED。这意味着它们共用一个事务上下文。只要内层方法抛出未捕获的运行时异常(如 RuntimeException),Spring 就会把整个事务标记为 rollback-only;哪怕外层 try-catch 吞掉异常,最后提交时也会报 Transaction rolled back because it has been marked as rollback-only。
- 删掉外层对内层调用的 catch 块,让异常穿透出去,由 Spring 统一回滚
- 如果必须捕获,catch 里要重新抛出(如 throw new RuntimeException(e))
- 若内层抛的是检查异常(如 IOException),需显式声明 @Transactional(rollbackFor = Exception.class)
需要隔离就用 REQUIRES_NEW
给内层方法加上 @Transactional(propagation = Propagation.REQUIRES_NEW),它就会挂起外层事务、新建一个完全独立的事务。内层失败回滚,不影响外层提交。
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 适合日志记录、消息通知等弱一致性操作
- 注意:会新开数据库连接,可能引发锁等待(比如外层已锁库存表,内层又写同一张表)
- 事务之间无状态共享,不能靠外层事务的变更影响内层逻辑
想局部回滚就选 NESTED
@Transactional(propagation = Propagation.NESTED) 利用数据库 Savepoint 实现真正的嵌套:外层事务执行中打个保存点,内层异常只回滚到该点,外层其余操作照常提交。
- 要求数据库支持 Savepoint(MySQL、PostgreSQL、Oracle 都支持)
- 不新建连接,比 REQUIRES_NEW 更轻量,也不会因锁表阻塞
- 典型场景:下单流程中,扣库存失败只回滚扣减动作,用户积分更新仍可保留
同个类里调用事务方法会失效
如果 A 方法和 B 方法都在同一个 Service 类里,A 直接调用 this.B(),B 的 @Transactional 注解不会生效。因为 Spring 事务基于代理机制,this.B() 走的是原始对象调用,绕过了代理拦截。
- 把 B 方法移到另一个 Service 类中,再注入调用
- 或者在当前类中注入自身 Bean(@Autowired private SelfService selfService;),然后用 selfService.b() 调用
- 也可通过 AopContext.currentProxy() 获取代理对象调用,但需开启 expose-proxy 配置
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










