本文目录导读:

Spring 的事务传播行为(Transaction Propagation)定义了当一个事务方法被另一个事务方法调用时,事务的边界应该如何扩散。
通俗地说,就是解决方法嵌套调用时,是共用同一个事务,还是各自独立事务,或者创建新事务的问题。
Spring 定义了 7种 传播行为(基于 Propagation 枚举),下面逐一说明。
核心分类(以是否支持当前事务为界)
REQUIRED(必须的)— 最常用,默认值
- 行为:如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。
- 场景:绝大多数业务方法适用。
A.save()调用B.save(),两者应在同一个事务中,要么都成功,要么都回滚。 - 示例:
@Transactional(propagation = Propagation.REQUIRED) public void methodA() { // ... methodB(); // methodB 会使用 methodA 的同一事务 }
SUPPORTS(支持的)
- 行为:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务方式执行。
- 场景:某些查询方法,有事务更好(能读已提交等),没有事务也能跑。
- 注意:非事务环境下执行时,可能导致脏读、不可重复读等问题。
MANDATORY(强制的)
- 行为:必须在现有事务中执行,如果当前没有事务,则直接抛出异常。
- 场景:该方法不允许被没有事务的调用者执行,确保调用方必须开启事务。
- 示例:
@Transactional(propagation = Propagation.MANDATORY)
核心分类(以创建新事务为主)
REQUIRES_NEW(总是新事务)— 常用
- 行为:无论当前是否存在事务,都挂起当前事务,创建一个全新的独立事务。
- 关键点:
- 新事务与外部事务互不影响,外层回滚,内层已提交则不受影响(需捕获异常);内层回滚,外层可以继续提交(除非捕获到内层异常)。
- 场景:
- 日志记录:业务回滚了,但操作日志必须写入(不能因为业务失败而丢失日志)。
- 发消息:业务失败了,但MQ消息已发送,不希望消息也被回滚。
- 示例:
@Transactional public void outer() { inner(); // 如果这里抛出异常,但 inner 已提交,则 outer 回滚不影响 inner } @Transactional(propagation = Propagation.REQUIRES_NEW) public void inner() { ... }
NOT_SUPPORTED(不支持事务)
- 行为:以非事务方式执行,如果当前存在事务,则挂起当前事务。
- 场景:包含一些不需要事务、且可能耗时较长的操作(如批量大文件处理),避免长时间占用数据库连接。
NEVER(绝不)
- 行为:以非事务方式执行,如果当前存在事务,则直接抛出异常。
- 场景:强制要求调用方不能有事务,例如某些必须在无事务环境下执行的清空数据操作(避免自动清空被回滚)。
NESTED(嵌套事务)— 特殊且强大
- 行为:如果当前存在事务,则在嵌套事务内执行(利用数据库的 Savepoint 保存点机制);如果当前没有事务,则行为同
REQUIRED。 - 关键点:
- 不是新事务:内层回滚,可以回滚到嵌套的保存点,不影响外层。
- 外层回滚:内层也一起回滚。
- 前提:数据库必须支持保存点(主流数据库如 MySQL、Oracle 都支持)。
- 与
REQUIRES_NEW的区别:REQUIRES_NEW:内层是独立事务,外层不能看到内层的未提交状态,且内层提交后不受外层影响。NESTED:内层是外层的一部分,外层能看到内层的未提交状态,内层回滚仅回滚到保存点,不结束外层事务。
- 场景:
- 批量操作,部分失败允许跳过:比如批量导入100条数据,第50条失败。
NESTED允许回滚到第50条之前,继续执行第51条;REQUIRES_NEW则第50条失败需要单独处理,且第50条之前的已提交(更复杂)。
- 批量操作,部分失败允许跳过:比如批量导入100条数据,第50条失败。
核心对比表
| 传播行为 | 是否有当前事务 | 行为 | 典型场景 |
|---|---|---|---|
| REQUIRED | 有 | 加入 | 默认选择,方法嵌套共享事务 |
| 无 | 新建 | ||
| SUPPORTS | 有 | 加入 | 查询方法,有事务更好,无事务也行 |
| 无 | 非事务运行 | ||
| MANDATORY | 有 | 加入 | 强制调用方必须有事务 |
| 无 | 抛出异常 | ||
| REQUIRES_NEW | 有 | 挂起当前,新建独立事务 | 日志、审计、消息发送 |
| 无 | 新建 | ||
| NOT_SUPPORTED | 有 | 挂起当前,非事务运行 | 耗时非事务操作 |
| 无 | 非事务运行 | ||
| NEVER | 有 | 抛出异常 | 强制无事务环境 |
| 无 | 非事务运行 | ||
| NESTED | 有 | 嵌套事务(保存点) | 批量处理,部分异常可跳过 |
| 无 | 新建(同REQUIRED) |
实战常见面试点
REQUIREDvsREQUIRES_NEW:前者是“同生共死”;后者是“各管各的”。NESTEDvsREQUIRES_NEW:前者外层回滚内层必回滚;后者外层回滚不影响内层(若内层已提交)。- 事务传播只在方法调用时生效:必须在代理对象(AOP代理,如Spring管理的Bean)之间调用才生效。同类内部方法调用(
this.xxx())不会触发事务传播(因为不走代理)。 - 默认值:
@Transactional不加参数时,默认是REQUIRED。