最近用网上开源项目芋道做公司业务的时候,偶尔复现一个bug(一步步debug很久),本人负责权限这一块,涉及各种用户权限继承转移,一个逻辑里面涉及权限的增删改,因此事务的一致性很重要,在这里重新回顾一下事务传播机制
Spring 事务传播机制方案总结
Spring 事务传播行为(Propagation Behavior)定义了客户端代码如何与现有事务边界进行交互。以下是主要的传播机制及其详细说明:
| 传播行为 | 描述 | 作用 | 存在事务 (TxA) | 不存在事务 | 发生异常时的回滚 |
|---|---|---|---|---|---|
| REQUIRED (默认) | 如果当前存在事务 TxA,则加入该事务;如果不存在,则创建一个新事务 TxB。 | 确保业务方法始终运行在一个事务上下文中。 | 加入 TxA。 | 创建新事务 TxB。 | TxA 或 TxB 标记为 rollback-only,并最终回滚。 |
| SUPPORTS | 如果当前存在事务 TxA,则加入该事务;如果不存在,则以非事务方式执行。 | 支持事务,但非强制性。通常用于查询操作。 | 加入 TxA。 | 非事务执行。 | 如果加入 TxA,TxA 会回滚。如果非事务执行,数据不会回滚。 |
| MANDATORY | 必须在一个已存在的事务 TxA 中执行;如果不存在事务,则抛出异常 (IllegalTransactionStateException)。 |
强制要求调用方提供事务上下文。 | 加入 TxA。 | 抛出异常。 | 加入 TxA,TxA 会回滚。 |
| REQUIRES_NEW | 总是挂起当前事务 TxA(如果存在),并创建一个全新的独立事务 TxB 来执行。 | 确保方法在完全独立的新事务中运行,与外部事务互不干扰。 | 挂起 TxA,创建 TxB。 | 创建新事务 TxB。 | TxB 独立回滚,TxA 的状态(挂起后恢复)不受影响。 |
| NOT_SUPPORTED | 总是以非事务方式执行;如果当前存在事务 TxA,则挂起该事务。 | 确保方法不使用事务。通常用于不涉及数据库写入的辅助操作。 | 挂起 TxA,非事务执行。 | 非事务执行。 | 不会触发事务回滚,因为没有事务上下文。 |
| NEVER | 总是以非事务方式执行;如果当前存在事务 TxA,则抛出异常 (IllegalTransactionStateException)。 |
严格禁止在事务中运行。 | 抛出异常。 | 非事务执行。 | 不会触发事务回滚。 |
| NESTED | 如果当前存在事务 TxA,则在其中创建一个逻辑上的嵌套事务(保存点 Savepoint)。如果外部 TxA 回滚,嵌套事务也会回滚。如果嵌套事务回滚,外部 TxA 可以选择不回滚。 | 提供局部的、可独立回滚的子事务。(依赖 JDBC Savepoint 特性,通常需使用 DataSourceTransactionManager) | 在 TxA 中创建保存点 TxB。 | 创建新事务 TxB(行为同 REQUIRED)。 | TxB 回滚时,TxA 可以捕滚到保存点;TxA 回滚时,TxB 必定回滚。 |
⚠️ 事务回滚的关键点:
- 外部回滚 vs 内部回滚 (REQUIRED):
- 如果使用
REQUIRED,内部方法抛出异常,会导致整个外部事务 (TxA) 被标记为rollback-only,最终外部事务提交时会抛出UnexpectedRollbackException。
- 如果使用
- 独立事务 (REQUIRES_NEW):
- 使用
REQUIRES_NEW时,如果内部事务 TxB 失败,只会回滚 TxB 的操作,而外部事务 TxA 会继续提交或回滚,两者完全隔离。
- 使用
- 嵌套事务 (NESTED):
NESTED创建的子事务依赖于父事务。子事务失败可以不影响父事务的提交(父事务可以捕滚到保存点),但父事务失败会强制子事务回滚。