Spring 事务传播行为全景分析表
📘 Spring 事务传播行为全景分析表(含代理与回滚细节)
✅ 一、A 和 B 在同一个类中
⚠️ 本类方法互调(如
this.b()
)不会经过 Spring 事务代理,B 的事务注解将不生效
1. A 无事务,B 为 @Transactional(REQUIRED)
情况 | 结果说明 |
---|---|
A 方法调用 B(this.b()) | B 无事务,不生效,相当于普通方法 |
B 方法报错 | 无事务可回滚,异常照常抛出或被处理 |
✅ 结论:B 的事务不生效,A/B 都无事务行为
2. A 为 @Transactional(REQUIRED)
,B 也为 @Transactional(REQUIRED)
情况 | 结果说明 |
---|---|
A 调用 B(this.b()) | B 事务不生效,相当于 B 方法加入 A 的事务上下文 |
A 在调用 B 前报错 | A 回滚,B 未调用 |
A 调用 B 后报错 | A 回滚,B 的数据也一起回滚 |
B 报错 | A 感知异常,回滚整个事务(包括 B 部分) |
✅ 结论:B事务注解无效,A 掌控整体事务
3. A 为 @Transactional(REQUIRED)
,B 为 @Transactional(REQUIRES_NEW)
情况 | 结果说明 |
---|---|
A 调用 B(this.b()) | B 的 REQUIRES_NEW 不生效,等价于 REQUIRED |
A 调用 B 后 A 报错 | A 回滚,B 数据也回滚 |
B 报错 | 异常回传,A 事务回滚,B 数据也回滚 |
✅ 结论:REQUIRES_NEW 无效,表现等价于 REQUIRED
✅ 二、A 和 B 在不同类中
🎯 方法调用通过 Spring 容器代理对象完成,事务注解均生效
1. A 为 @Transactional(REQUIRED)
,B 为 @Transactional(REQUIRED)
情况 | 结果说明 |
---|---|
A 调用 B 前出错 | A 回滚,B 未执行 |
A 调用 B 后出错 | A 回滚,B 作为同一个事务,数据也回滚 |
B 报错(运行时异常) | 整个事务回滚(A + B) |
B 报错(被 A 捕获) | 若未手动标记 rollbackOnly,A 将正常提交 ⚠️ |
✅ 结论:A、B 属于同一个事务体,彼此影响
2. A 为 @Transactional(REQUIRED)
,B 为 @Transactional(REQUIRES_NEW)
情况 | 结果说明 |
---|---|
A 调用 B 前出错 | A 回滚,B 未执行 |
A 调用 B 后出错 | A 回滚,B 独立事务 已提交,不回滚 |
B 报错(运行时异常) | B 回滚,A 接收异常是否回滚取决于是否捕获异常 |
B 报错,A 捕获异常 | A 正常提交,B 回滚 |
B 报错,A 未捕获异常 | A 回滚(由于异常传递),B 已回滚,不受影响 |
✅ 结论:B 与 A 为完全独立事务,异常控制是否影响 A 看是否被捕获
🧠 总结对比表
场景 | A 是否回滚 | B 是否回滚 | 说明 |
---|---|---|---|
同类,A无事务,B为REQUIRED | ❌ | ❌ | B事务无效 |
同类,A为REQUIRED,B为REQUIRED | ✅ | ✅ | B事务失效,实际加入A |
同类,A为REQUIRED,B为REQUIRES_NEW | ✅ | ✅ | B事务失效,表现等价于REQUIRED |
不同类,A为REQUIRED,B为REQUIRED | ✅ | ✅ | 同事务体,共生共死 |
不同类,A为REQUIRED,B为REQUIRES_NEW,A未捕获B异常 | ✅ | ✅(B先回滚) | 异常传递导致A回滚 |
不同类,A为REQUIRED,B为REQUIRES_NEW,A捕获B异常 | ❌ | ✅ | A正常提交,B单独回滚 |
📌 建议:
-
若希望 B 独立提交或失败不影响 A,必须在不同类中使用
REQUIRES_NEW
并合理处理异常; -
若希望 所有操作属于一个事务体,使用默认
REQUIRED
,确保 B 被代理; -
⚠️ 尽量避免在同一个类中事务自调用,或使用
ApplicationContext.getBean(this.getClass()).b()
替代this.b()
来绕过代理问题。