Spring无“自定义自动关闭传播阻断器”,非受检异常默认回滚,需用REQUIRES_NEW隔离事务或NESTED+保存点实现局部回滚,而非try-catch拦截异常。

明确非受检异常的默认回滚规则
Spring 默认对 RuntimeException 及其子类、Error 自动触发回滚,比如 IllegalArgumentException、NullPointerException。但像 IOException 这类检查型异常,默认不回滚,除非显式配置 rollbackFor = IOException.class。
关键点在于:异常是否回滚,取决于它是否逃逸出当前事务作用域,而不是有没有被 catch。
用 REQUIRES_NEW 隔离内层事务
这是最稳妥、最常用的做法——让嵌套逻辑运行在完全独立的事务中,天然切断 rollback-only 状态传递:
- 内层方法加
@Transactional(propagation = Propagation.REQUIRES_NEW) - 即使抛出
ServiceException extends RuntimeException,也只回滚自身事务,外层无感知 - 必须保证是跨 Bean 调用(不能 this.xxx()),否则代理失效,事务不生效
用 NESTED + Savepoint 实现局部回滚
适合需要共享数据库连接、且底层支持保存点的场景(如 MySQL InnoDB):
- 外层用
REQUIRED,内层用NESTED - 在内层捕获异常后,调用
TransactionStatus.setRollbackOnly()或手动回滚到保存点 - 这样外层仍可提交,仅内层修改被撤销,例如批量导入时单条失败不影响其余记录
别踩 try-catch 清除 rollback-only 的坑
常见错误写法:
- 外层
REQUIRED,内层抛CustomBizException extends RuntimeException - 外层用
try-catch吞掉异常 —— 无效!事务状态已标记为 rollback-only - 最终外层提交时仍会抛
UnexpectedRollbackException
REQUIRES_NEW 或 NESTED。

















