嵌套事务回滚取决于传播行为而非catch:required共享事务,内层异常致外层unexpectedrollbackexception;requires_new隔离事务,内层失败不影响外层;nested基于savepoint局部回滚,依赖数据库支持。

嵌套方法调用时事务是否回滚、回滚到哪一层,不取决于你有没有 catch 异常,而取决于 传播行为怎么配。默认的 REQUIRED 看似简单,但最容易引发 UnexpectedRollbackException——外层 catch 了内层异常,结果提交时才发现事务早已被标记为 rollback-only。
REQUIRED:共享事务,一荣俱荣、一损俱损
这是默认行为,也是多数人“以为安全”实则风险最高的选择。
- 内外方法都用
@Transactional(没写 propagation)或显式写Propagation.REQUIRED,它们共用同一个物理事务和TransactionStatus实例 - 只要内层抛出未捕获的
RuntimeException,Spring 就会标记该事务为rollback-only - 外层即使
try-catch吞掉异常,也无法清除这个标记;等方法结束要 commit 时,直接抛UnexpectedRollbackException - 适用场景:强一致性流程,比如“下单 + 扣库存 + 写订单日志”,必须全部成功或全部失败
REQUIRES_NEW:彻底隔离,各管各的
需要内层失败不影响外层提交时,这是最直接可靠的解法。
你接手一个需求时,是不是经常这样:"改一个小字段 → 结果把不相关的代码也改了"、"需求说得很模糊 → 做着做着发现跑偏了"、"数据库加个字段 → 上线才发现漏了数据兼容"、"做了个新功能 → 一个月后自己都忘了当时怎么设计的"、"写到一半被中断 → 再回来不知道从哪继续";这个 Skill 就是专门解决这些问题的。 它会自动探测你的项目环境(JDK、Spring Boot、ORM、数据库、前端框架),记在
- 内层方法标注
@Transactional(propagation = Propagation.REQUIRES_NEW),Spring 会挂起当前事务,开启全新事务(新连接、新事务 ID) - 内层回滚只影响自己,外层照常提交;外层回滚也不会撤回内层已提交的数据
- 注意:必须跨 Bean 调用(不能
this.methodB()),否则代理失效,退化为普通方法调用 - 典型用途:审计日志、消息发送、补偿任务等“尽力而为”型操作
NESTED:局部回滚,省连接、靠 savepoint
想在同一个数据库连接里实现“单条失败不中断批量处理”,就选它。
- 内层用
Propagation.NESTED,Spring 在当前事务内建 JDBCSavepoint,不新开事务也不占新连接 - 内层异常可回滚到该保存点,外层继续执行并最终提交(除非外层自己也出错)
- 依赖数据库支持:MySQL InnoDB、PostgreSQL 可用;MyISAM、部分云数据库旧版可能不支持
- 验证方式:运行时捕获
SQLException,看是否提示 “savepoint does not exist” 或 “feature not supported”
别踩这些坑
传播行为不是魔法开关,配错或用错调用方式,效果就完全走样。
-
SUPPORTS、NOT_SUPPORTED这类不开启事务的行为,根本无法控制内层回滚逻辑,排除 - 同类内自调用(
this.xxx())会导致代理失效,REQUIRES_NEW和NESTED都会退化为普通方法,事务控制失效 - 仅靠
try-catch无法挽救REQUIRED嵌套中的事务状态——状态已被污染,catch 只是掩盖了问题 - 高并发下慎用过多
REQUIRES_NEW,需确认连接池(如 HikariCP)配置充足,避免连接耗尽
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










