出现原因
Spring 框架的默认事务传播方式是 PROPAGATION_REQUIRED
(内层事务加入外层事务中)
在内层事务因异常结束时, Spring 会把事务标记为 rollback-only
. 这时如果外层事务 catch 捕捉了异常 e, 那么外层事务方法还会继续执行代码, 直到外层事务结束.
Spring 发现事务已经被标记为 rollback-only
, 外层方法却正常执行成功, 这时 Spring 就会抛出 UnexpectedRollbackException
org.springframework.transaction.UnexpectedRollbackException:Transaction rolled back because it has been marked as rollback-only
解决方案
- 希望内层事务抛出异常时 中断程序执行, 在外层事务的 catch 代码块中继续抛出异常 e
- 如果希望程序正常执行完, 并且希望外层事务结束时 全部提交, 则在内层事务方法中将异常 e 捕获并处理
- 如果希望 内层事务回滚, 但不影响外层事务提交, 需要将内层事务的传播方式指定为
PROPAGATION_NESTED
. (ps:PROPAGATION_NESTED
基于数据库savepoint
实现的嵌套事务, 外层事务的提交和回滚能够控制嵌内层事务, 而内层事务报错时, 可以返回原始savepoint
, 外层事务可以继续提交.