跳到主要内容

事务传播、隔离与回滚规则

传播方式决定方法进入时如何处理已有事务,隔离级别决定数据库并发读写可见性,回滚规则决定异常如何影响事务结果。这三组配置作用在不同层面,需要分别验证。

1. REQUIRED 复用同一个物理事务

默认 PROPAGATION_REQUIRED 在已有事务时加入,没有时新建。内外方法各自是逻辑事务范围,但共享同一个物理事务。

内层把事务标记为 rollback-only 后,即使外层捕获异常并正常返回,最终提交也会回滚,并可能抛出 UnexpectedRollbackException。这避免调用方误以为提交成功。

2. REQUIRES_NEW 使用独立物理事务

已有事务被挂起,内层取得自己的连接和事务:

@Transactional(propagation = Propagation.REQUIRES_NEW)
public void recordAudit(AuditEvent event) {}

内层可以独立提交或回滚,并在完成后释放自己的锁。外层仍持有资源,内层还要再向连接池申请连接;并发线程都这样做时,池大小不足会互相等待。

它适合确实要求独立提交的审计等场景,但也意味着外层失败时内层结果仍保留。

3. NESTED 依赖同一事务的保存点

PROPAGATION_NESTED 通常在一个物理 JDBC 事务中建立 savepoint。内层失败可以回滚到保存点,让外层决定是否继续;外层最终回滚时,内层变化仍会一起回滚。

它不等同于独立事务,并非所有事务管理器都支持。Spring 文档明确指出典型支持来自 JDBC DataSourceTransactionManager

4. 其他传播方式表达调用前置条件

传播方式已有事务没有事务
SUPPORTS加入非事务执行
MANDATORY加入抛异常
NOT_SUPPORTED挂起非事务执行
NEVER抛异常非事务执行

是否真的能挂起取决于事务管理器。大多数业务方法使用少数几种传播方式,复杂组合需要集成测试证明。

5. 隔离级别由新物理事务落实

Isolation.DEFAULT 使用数据库默认值。指定隔离级别通常只在当前调用实际创建新事务时生效;加入已有事务时,内层不能凭注解把同一物理事务换成另一隔离级别。

可以启用严格验证让不兼容声明尽早失败。隔离级别解决数据库并发可见性,不处理远程服务或缓存的一致性。

6. 默认回滚 RuntimeException 与 Error

声明式事务默认对未处理的 RuntimeExceptionError 回滚,对普通受检异常不自动回滚。业务需要时显式声明:

@Transactional(rollbackFor = PaymentException.class)

更重要的是让异常离开代理调用。目标方法内部捕获后返回成功,拦截器看不到失败;如果需要回滚,应继续抛出或显式设置 rollback-only,并让调用方知道结果。

7. 常见问题

7.1 内层 REQUIRES_NEW 提交后,外层回滚会撤销它吗

不会,它们是独立物理事务。业务必须接受内层记录单独存在,或设计后续补偿。

7.2 NESTED 内层回滚以后外层一定能提交吗

不一定。外层可以继续,但还要看异常处理、数据库错误是否使整个事务失效,以及事务管理器是否支持保存点。

8. 面试题

8.1 REQUIRES_NEWNESTED 有什么区别

出现公司:阿里云、美团

考察重点

  • 独立物理事务与同事务保存点。
  • 外层回滚的影响。
  • 连接池与事务管理器支持。

相关内容:第 2 节“REQUIRES_NEW 使用独立物理事务”和第 3 节“NESTED 依赖同一事务的保存点”。

参考回答

REQUIRES_NEW 挂起外层并创建独立物理事务,内层提交后不受外层回滚影响,但会额外占用连接。NESTED 通常在同一 JDBC 事务中建立 savepoint,内层可以局部回滚,外层最终回滚仍会撤销全部变化。

两者还受 TransactionManager 能力限制。REQUIRES_NEW 要评估连接池耗尽,NESTED 要确认保存点支持,不能只按注解名称选择。