Skip to content

Spring 有哪几种事务传播行为? ​

  • 标签:后端、Spring

🏛 业务架构师 ​

事务传播行为定义"一个事务方法被另一个事务方法调用时,事务边界怎么定"。七种里我重点讲业务含义:

  • REQUIRED(默认):有事务就加入,没有就新建。绝大多数业务动作的正确选择——订单创建和库存扣减同生共死;
  • REQUIRES_NEW:挂起当前事务,开独立新事务。我用它的场景是审计日志:主业务回滚了,审计也必须留下"有人尝试过且失败了"的记录;还有消息通知,通知失败不能拖垮主流程。注意它会占第二个数据库连接,连接池要预留余量,否则高并发下死锁在连接池上;
  • NESTED:保存点语义,部分回滚。适合批量处理中单条失败不影响整体——批量导入 100 条,第 37 条出错回滚到保存点继续;
  • SUPPORTS / NOT_SUPPORTED / NEVER / MANDATORY:跟随/挂起/拒绝/强制,多为契约性声明,比如 MANDATORY 用来强制调用方必须先开事务,把隐性契约显性化。

业务上的两类高频事故:

  1. 同类内部自调用导致传播行为失效(没走代理),REQUIRED 变成"裸奔",记住:事务注解只对跨代理的调用生效;
  2. REQUIRES_NEW 的连接池耗尽——高峰期两个连接互相等待,池死锁。

我的规约:主子事务边界 = 业务边界,事务方法保持小而外显,嵌套事务必须在设计文档里写明理由。

🏗 系统架构师 ​

事务传播行为回答"方法嵌套调用时,内层事务如何与外层事务相处",7 种我分三组记:

以当前事务为准:

  • REQUIRED(默认):有事务加入,没有就新建;
  • SUPPORTS:有就加入,没有就非事务跑;
  • MANDATORY:必须有事务,否则抛异常。

新旧隔离:

  • REQUIRES_NEW:挂起当前事务,开独立新事务,内层回滚不影响外层已挂起的事务,外层回滚也不影响已提交的内层——日志/审计必须留下痕迹的场景用它;
  • NOT_SUPPORTED:挂起事务,以非事务方式执行;
  • NEVER:有事务就抛异常。

嵌套:

  • NESTED:通过**保存点(savepoint)**在当前事务内打标,内层回滚只回到保存点,外层可选择继续;外层回滚则内层一并回滚。

踩坑经验:REQUIRES_NEW 与 NESTED 的区别是两个独立事务 vs 同一事务的局部回滚;同类的自调用(this.method())不经过代理,注解全部失效,这是我排查"事务不生效"的第一嫌疑;NESTED 依赖 JDBC 保存点,JPA/某些数据源下受限,用前要验证。

💻 全栈开发专家 ​

7 种,我重点讲项目里真正用到的四种:

  • REQUIRED(默认):当前有事务就加入,没有就新建。绝大多数业务用它。
  • REQUIRES_NEW:挂起当前事务,开一个独立新事务,内外互不影响(内回滚不影响外,外回滚不影响已提交的内)。场景:主流程失败也要记录操作日志。
  • NESTED:嵌套事务(savepoint),内层回滚到保存点,外层可选择继续;外层回滚则内层必滚。场景:批量处理里单条失败不影响整批。
  • SUPPORTS / NOT_SUPPORTED / MANDATORY / NEVER:有就用/没有就挂起/必须有/必须没有,做事务边界的防御性约束。

我踩过的两个坑(比背枚举有价值):

  1. 自调用失效:同类里 methodA() 调 this.methodB(),@Transactional(REQUIRES_NEW) 的 B 不生效——绕过了代理对象。解法:拆 Bean 或注入自身代理。
  2. REQUIRES_NEW 耗尽连接池:外层事务占 1 个连接,内层新事务再借 1 个,并发一高连接池打满。我在压测时遇到过,最后限制并发 + 外层尽量无事务。

还有:传播行为是声明式事务(Spring)的概念,数据库本身只有事务和 savepoint;@Transactional 默认只回滚 RuntimeException 和 Error,检查异常要 rollbackFor。