Spring 事务传播机制详解:7 种类型对比+实际场景(附避坑指南)

一、事务传播的本质

事务传播的核心是:当多个事务方法相互调用时,如何管理事务的边界
Spring 通过 @Transactional(propagation = Propagation.XXX) 注解来控制这一行为,背后依赖数据库的“事务管理器”(如 DataSourceTransactionManager)。

二、7 种传播行为的详细解释(附代码)

1.REQUIRED(默认)

行为:

  • 如果当前有事务,则加入当前事务

  • 如果当前没有事务,则新建一个事务

代码示例:

@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {
    // 操作数据库
    methodB(); // 调用另一个事务方法
}

@Transactional(propagation = Propagation.REQUIRED)
public void methodB() {
    // 如果 methodA 有事务,methodB 会加入它;否则自己新建事务
}

典型场景:商城下单操作,下单和库存操作要么都成功、要么都失败

2.REQUIRES_NEW

行为:

  • 无论如何都会新建一个事务,如果当前有事务则会将原来事务进行挂起

代码示例:

@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {
    // 主事务操作
    try {
        methodB(); // 调用 REQUIRES_NEW 方法
    } catch (Exception e) {
        // 即使 methodB 失败,methodA 可以继续
    }
}

@Transactional(propagation = Propagation.REQUIRES_NEW)
public void methodB() {
    // 强制开启新事务,与 methodA 的事务无关
}

典型场景:日志记录,无论其他业务是否成功都需要进行日志记录

3.NESTED

行为:

  • 如果当前有事务,则创建一个嵌套事务

  • 如果当前没有事务,等同于REQUIRED

代码示例:

@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {
    // 主事务操作
    try {
        methodB(); // 嵌套事务
    } catch (Exception e) {
        // 只回滚 methodB 的操作,methodA 继续
    }
}

@Transactional(propagation = Propagation.NESTED)
public void methodB() {
    // 嵌套事务(可以部分回滚)
}

典型场景:

主订单(主事务)和订单详情(嵌套事务),无论订单详情是否成功都不会影响主订单

4.SUPPORTS

行为:

  • 如果当前有事务就加入

  • 如果当前没有事务就以无事务运行

典型场景:

查询数据,有事务就加入,没有事务也无所谓

5.MANDATORY

行为:

  • 必须在事务中运行,否则会报异常

典型场景:

资金结算相关场景,要求必须在事务中运行

6.NOT_SUPPORTED

行为:

  • 以非事务方式运行,如果有事务则挂起处理

典型场景:

发送短信通知,无论业务是否成功都发送

7.NEVER

行为:

  • 必须在非事务中运行,如果有事务则抛异常

典型场景:

数据校验方法,必须在非事务中运行

三、关键原理剖析

1. 事务的底层实现

  • Spring 事务本质是通过 AOP 代理 实现的,调用事务方法时,会通过代理对象控制事务的开启、提交或回滚。

  • 事务传播的核心逻辑

    • 根据传播类型,决定是否复用已有事务、挂起事务或抛出异常。

    • 例如 REQUIRES_NEW 会调用 doBegin() 方法强制开启新事务,并挂起旧事务。

2. 嵌套事务(NESTED)的特殊性

  • 嵌套事务依赖数据库的 保存点(Savepoint) 机制(如 MySQL 的 InnoDB 支持)。

  • 嵌套事务的回滚只会回滚到保存点,而外层事务可以继续。

  • 注意:如果数据库不支持保存点,NESTED 会退化为 REQUIRED

3. 事务挂起与恢复

  • 当调用 REQUIRES_NEW 或 NOT_SUPPORTED 时,Spring 会挂起当前事务,将事务信息暂存到线程局部变量中,待新事务执行完毕后恢复。


四、实际开发中的坑

1. 事务不生效的常见原因

  • 内部调用:同一个类中的方法调用(如 this.methodB())不会触发 AOP 代理,导致事务失效。
    解决方案:通过 AopContext.currentProxy() 获取代理对象调用。

  • 异常被捕获:如果事务方法中的异常被 try-catch 捕获且未重新抛出,事务不会回滚。
    解决方案:在 catch 中手动回滚或抛出异常。

2. REQUIRES_NEW 的性能问题

  • 频繁开启新事务会增加数据库连接池的压力,需谨慎使用。


五、面试高频问题

问题1:REQUIRED 和 NESTED 有什么区别?

  • REQUIRED:内外方法共用同一个事务,任何一个方法回滚会导致整个事务回滚。

  • NESTED:外层方法事务回滚会导致嵌套事务回滚,但嵌套事务可以独立回滚(通过保存点机制)。

问题2:事务方法中调用另一个事务方法,为什么事务不生效?

  • 可能是内部调用(未通过代理对象),或异常被捕获未抛出。


总结表格

传播类型行为描述(简版)适用场景是否独立事务?是否受外部事务影响?高频搜索关键词关联
REQUIRED默认!有事务则加入,无事务则新建订单+库存操作、普通业务逻辑✅(整体回滚)Spring事务传播、默认事务
REQUIRES_NEW强制新建事务,挂起外部事务日志记录、异步任务❌(完全独立)独立事务、事务隔离
NESTED嵌套事务(基于保存点),外部事务回滚会连带嵌套事务,嵌套事务可单独回滚订单主表+详情表部分✅✅(主事务影响子)嵌套事务、保存点
SUPPORTS有事务则用,没有则以非事务运行查询操作事务与非事务切换
NOT_SUPPORTED强制非事务运行,挂起外部事务发送短信、通知❌(非事务)非事务执行
MANDATORY必须在事务中运行,否则抛异常资金结算、核心业务强制事务、事务校验
NEVER必须无事务运行,否则抛异常数据校验、非事务方法❌(非事务)禁止事务、非事务调用

掌握事务传播机制,能让你在分布式系统和高并发场景下游刃有余。建议在开发中多用 REQUIRED 和 REQUIRES_NEW,谨慎使用其他类型。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

yiridancan

你的鼓励师我创造最大的动力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值