Appearance
Spring 事务传播行为怎么理解:为什么有些回滚生效,有些却没有
Spring 事务最容易让人困惑的地方不是不会写 @Transactional,而是:
- 明明加了事务
- 结果回滚没生效
- 或者嵌套调用后行为和预期不一样
先说结论
Spring 事务真正关键的不是注解本身,而是:
- 事务边界落在哪里
- 方法调用有没有走代理
- 异常有没有真正抛出来
- 传播行为是不是符合你的业务意图
一、事务传播在解决什么问题
可以把它理解成:
- 一个事务方法调用另一个事务方法时,到底要不要共用同一个事务
这件事在订单、库存、优惠券、日志等场景里非常常见。
二、为什么很多人对传播行为没感觉
因为日常最常用的还是默认行为。
只有在这些场景里,传播行为才会变得特别重要:
- 主业务失败是否要带着子流程一起回滚
- 审计日志是否要独立提交
- 某一步失败是否允许外层继续
三、最常见的几个坑
1. 方法内部自调用导致事务失效
这是 Spring 事务最经典的坑之一。
因为事务通常依赖代理生效,如果类内部自己调自己,很多时候就不会走代理。
2. 捕获异常后不再抛出
如果异常被吃掉了,事务通常也就不知道该回滚了。
3. 以为所有异常都会自动回滚
实际使用时一定要明确:
- 你的异常类型
- 你的回滚规则
4. 把一个很长的业务流程包进一个大事务
这样很容易带来:
- 锁持有时间过长
- 冲突放大
- 死锁概率上升
四、一个更实用的理解方式
不要先背传播行为定义,而是先问:
- 这几个步骤是不是一条业务原子链路
- 失败后我到底想一起回滚,还是局部保留
如果答案不同,传播行为的选择就会清楚很多。
五、什么时候要特别小心
- 事务里调远程接口
- 事务里做耗时操作
- 一个主流程里混入日志、通知、消息发送
这些场景都很容易让事务边界和预期不一致。
一句话总结
Spring 事务传播行为真正要解决的,不是“注解怎么配”,而是:
- 哪些步骤要绑在一起
- 哪些步骤应该拆开
把事务边界想明白,很多“为什么没回滚”的问题都会更容易解释。