SpringBoot 7种事务传播机制解析

Spring Boot 本身不实现事务,底层用的是 Spring Framework 的事务管理。所谓“7 种事务传播机制”,就是 @Transactional(propagation = ...) 中的 Propagation 枚举:

@Transactional(propagation = Propagation.REQUIRED)
public void method() {}

7 种分别是:REQUIRED、SUPPORTS、MANDATORY、REQUIRES_NEW、NOT_SUPPORTED、NEVER、NESTED。

核心判断逻辑是:当前线程是否已经存在事务?被调用方法希望如何对待这个事务?


1. REQUIRED:默认,有则加入,无则新建

@Transactional(propagation = Propagation.REQUIRED)
  • 当前有事务:加入当前事务,成为同一个事务。
  • 当前无事务:新建一个事务。
  • 这是 Spring 默认传播行为。

特点:

  • 内外层通常一起提交、一起回滚。
  • 内层抛异常,即使外层 try-catch 了,也可能因为事务被标记 rollback-only,最终抛 UnexpectedRollbackException。
  • 适合绝大多数增删改业务。

场景:订单创建、用户注册、支付扣款等。


2. SUPPORTS:有则加入,无则以非事务执行

@Transactional(propagation = Propagation.SUPPORTS)
  • 当前有事务:加入当前事务。
  • 当前无事务:不新建事务,以非事务方式执行。

特点:

  • 不强制事务。
  • 适合查询方法。
  • 如果被事务方法调用,它就在事务里;如果被非事务方法调用,它就不在事务里。

场景:查询、只读操作。

常配合:

@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)

3. MANDATORY:必须存在事务,否则报错

@Transactional(propagation = Propagation.MANDATORY)
  • 当前有事务:加入当前事务。
  • 当前无事务:抛异常 IllegalTransactionStateException。

特点:

  • 强制要求调用方已经开启事务。
  • 自己不会新建事务。

场景:某些内部核心方法,必须由外层事务统一控制,不允许被随意单独调用。


4. REQUIRES_NEW:总是新建独立事务

@Transactional(propagation = Propagation.REQUIRES_NEW)
  • 当前有事务:挂起当前事务,新建一个独立事务。
  • 当前无事务:新建一个事务。

特点:

  • 内外事务相互独立。
  • 内层提交后,即使外层回滚,内层已提交的数据通常不会回滚。
  • 内层回滚后,如果异常被外层捕获,外层可以继续提交。
  • 注意数据库锁:外层事务可能持有锁,内层新事务再操作同一行,可能死锁或等待。

场景:

  • 日志记录、审计记录。
  • 失败也不影响主流程的独立操作。
  • 主业务回滚,但某些记录必须保留。

示例:

@Transactional
public void createOrder() {
    orderMapper.insert(order);

    try {
        logService.saveLog(); // REQUIRES_NEW
    } catch (Exception e) {
        // 日志失败不影响订单主事务
    }
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void saveLog() {
    logMapper.insert(log);
}

5. NOT_SUPPORTED:以非事务方式执行,挂起当前事务

@Transactional(propagation = Propagation.NOT_SUPPORTED)
  • 当前有事务:挂起当前事务,自己以非事务方式执行。
  • 当前无事务:以非事务方式执行。

特点:

  • 自己不在事务中运行。
  • 适合不希望被事务包裹的操作。
  • 如果操作失败,不会触发当前事务回滚,但当前事务被挂起期间,数据库连接和锁可能仍然存在。

场景:

  • 发送消息、调用远程接口、耗时操作。
  • 不希望长时间占用事务。

6. NEVER:必须无事务,否则报错

@Transactional(propagation = Propagation.NEVER)
  • 当前有事务:抛异常 IllegalTransactionStateException。
  • 当前无事务:以非事务方式执行。

特点:

  • 和 MANDATORY 相反。
  • 强制方法不能在事务中运行。

场景:

  • 某些工具方法、校验方法,明确禁止事务。
  • 避免被外层事务误包裹。

7. NESTED:嵌套事务,基于保存点 Savepoint

@Transactional(propagation = Propagation.NESTED)
  • 当前有事务:创建嵌套事务,本质是设置一个保存点 Savepoint。
  • 当前无事务:新建事务,类似 REQUIRED。

特点:

  • 内层回滚只会回滚到保存点,外层可以继续。
  • 外层回滚会连带内层一起回滚。
  • 内层提交并不是真正提交,只是释放保存点,最终仍由外层决定提交或回滚。
  • 需要底层资源支持保存点,例如 JDBC 的 DataSourceTransactionManager。
  • JPA、Hibernate、JTA 等是否支持,要看具体实现和方言。
  • 与 REQUIRES_NEW 最大区别:NESTED 不挂起外层事务,是同一个物理事务中的保存点;REQUIRES_NEW 是独立的新事务。

示例:

@Transactional
public void outer() {
    // 业务 A
    try {
        innerService.inner();
    } catch (Exception e) {
        // 内层回滚到保存点,外层可以继续
    }
    // 业务 B
}
@Transactional(propagation = Propagation.NESTED)
public void inner() {
    // 业务 C
}

七种传播机制速查表

传播行为 当前有事务 当前无事务 是否挂起当前事务 典型场景
REQUIRED 加入当前事务 新建事务 否 默认,大多数增删改
SUPPORTS 加入当前事务 非事务执行 否 查询
MANDATORY 加入当前事务 抛异常 否 强制要求外层有事务
REQUIRES_NEW 挂起当前事务,新建独立事务 新建事务 是 日志、审计、独立业务
NOT_SUPPORTED 挂起当前事务,非事务执行 非事务执行 是 远程调用、耗时操作
NEVER 抛异常 非事务执行 否 禁止事务
NESTED 创建保存点,嵌套事务 新建事务 否 部分回滚,外层可继续

REQUIRED、REQUIRES_NEW、NESTED 的区别

这是面试和实战最常问的。

REQUIRED

内外同一个事务:

  • 内层异常,通常整体回滚。
  • 外层异常,整体回滚。
  • 不能做到“内层失败但外层继续提交”。

REQUIRES_NEW

内外两个独立事务:

  • 内层异常回滚,外层捕获后可以继续。
  • 内层成功提交后,外层回滚不影响内层。
  • 会挂起外层事务,可能产生锁竞争。

NESTED

同一个物理事务中的嵌套事务,基于保存点:

  • 内层回滚到保存点,外层可以继续。
  • 外层回滚,内层也会回滚。
  • 内层不是真正独立提交。
  • 依赖底层保存点支持。

Spring Boot 使用示例

@Service
public class OrderService {

    @Autowired
    private LogService logService;

    @Transactional(rollbackFor = Exception.class)
    public void createOrder() {
        // 主业务
        // orderMapper.insert(...);

        try {
            logService.saveLog();
        } catch (Exception e) {
            // 日志失败不影响订单
        }
    }
}

@Service
public class LogService {

    @Transactional(
        propagation = Propagation.REQUIRES_NEW,
        rollbackFor = Exception.class
    )
    public void saveLog() {
        // logMapper.insert(...);
    }
}

注意:OrderService 必须通过 Spring 代理调用 LogService,传播行为才生效。


常见坑

  1. 默认只对 RuntimeException 和 Error 回滚

    @Transactional(rollbackFor = Exception.class)

    受检异常默认不回滚。

  2. 同类内部调用事务失效

    public void a() {
       this.b(); // b 上的 @Transactional 不生效
    }

    因为没走 Spring 代理。

  3. 异常被吞掉,事务不会回滚

    try {
       // 业务
    } catch (Exception e) {
       // 没抛出,也没标记回滚
    }
  4. 多线程事务不传播
    Spring 事务基于 ThreadLocal,新线程不会继承当前事务。

  5. NESTED 不一定所有数据库/ORM 都支持
    它依赖保存点,JDBC 支持较好,JPA/JTA 要看具体实现。

  6. REQUIRES_NEW 可能死锁
    外层事务未提交,持有数据库锁,内层新事务操作同一数据可能等待或死锁。

  7. 数据库引擎要支持事务
    例如 MySQL 的 MyISAM 不支持事务。


选择建议

  • 大多数业务:用默认 REQUIRED。
  • 查询:SUPPORTS + readOnly = true。
  • 必须在外层事务中运行:MANDATORY。
  • 独立提交、不影响主事务:REQUIRES_NEW。
  • 不想被事务包裹:NOT_SUPPORTED。
  • 禁止事务:NEVER。
  • 需要部分回滚、外层可继续:NESTED,但要确认底层支持保存点。

一句话总结:
REQUIRED 是默认同生共死;REQUIRES_NEW 是另起炉灶;NESTED 是保存点回滚;SUPPORTS 是随缘;MANDATORY 是必须有;NOT_SUPPORTED 是挂起不用;NEVER 是绝对不要。

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注

©2018-2026 Howell版权所有 备案号:冀ICP备19000576号