bigywater.tech
Spring · 31

回滚规则:检查异常、RuntimeException 与 rollbackFor

@Transactional 抛了异常就一定回滚吗?受检异常呢?异常被 catch 吞掉呢?rollbackFor 和 noRollbackFor 同时命中谁说了算?这篇在 Spring Framework 7.0.9 和 MySQL 26.7.0 上逐个量出来。

提交
抛受检异常 IOException 且没有写 rollbackFor 时的结果(表里剩 1 行)
提交
方法内部 catch 吞掉异常时的结果(事务不知道出过错)
更近的赢
rollbackFor 与 noRollbackFor 同时匹配时继承层次上更近的那条规则决定

默认规则

实测 · 本机执行输出
  抛 RuntimeException                             → 调用者看到 IllegalStateException                        表里剩 0 行 → 回滚
  抛 Error(AssertionError)                        → 调用者看到 AssertionError                               表里剩 0 行 → 回滚
  抛受检异常 IOException(默认)                          → 调用者看到 IOException                                  表里剩 1 行 → 提交
  抛受检异常,rollbackFor = Exception.class            → 调用者看到 IOException                                  表里剩 0 行 → 回滚
  抛 IOException,rollbackFor = IOException.class  → 调用者看到 IOException                                  表里剩 0 行 → 回滚
  抛 IOException,rollbackForClassName             → 调用者看到 IOException                                  表里剩 0 行 → 回滚
  抛 RuntimeException,noRollbackFor               → 调用者看到 IllegalStateException                        表里剩 1 行 → 提交

RuntimeException 与 Error 回滚,受检异常提交;rollbackFor 声明受检异常也回滚。

包装与吞掉

实测 · 本机执行输出
  受检异常包成 RuntimeException 再抛                     → 调用者看到 RuntimeException ← IOException               表里剩 0 行 → 回滚
  方法内部 catch 吞掉异常                                → 调用者看到 方法正常返回                                       表里剩 1 行 → 提交
  吞掉异常,但 setRollbackOnly()                       → 调用者看到 方法正常返回                                       表里剩 0 行 → 回滚

包成 RuntimeException 会回滚,catch 吞掉会提交,setRollbackOnly() 手动回滚。

规则冲突

实测 · 本机执行输出
  抛 MyChild(rollbackFor = MyChecked.class)       → 调用者看到 MyChild                                      表里剩 0 行 → 回滚
  rollbackFor = MyChecked,noRollbackFor = MyChild,抛 MyChild → 调用者看到 MyChild                                      表里剩 1 行 → 提交
  rollbackFor = MyChild,noRollbackFor = MyChecked,抛 MyChild → 调用者看到 MyChild                                      表里剩 0 行 → 回滚
  rollbackFor = RuntimeException,noRollbackFor = MyRuntime,抛 MyRuntime → 调用者看到 MyRuntime                                    表里剩 1 行 → 提交

两条规则都匹配时,继承层次上更近的一条赢。

分步演示

事务的回滚规则
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / OpenJDK 21.0.12(Homebrew) / Spring Boot 4.0.8(Spring Framework 7.0.9、Tomcat 11.0.24) / Spring Cloud 2025.1.3;用 javac 编译、java 直接运行,依赖由 Maven 从 Maven Central 解析;数字每次运行会有小幅抖动。用不带 Boot 的 Spring Framework 7.0.9 的 @EnableTransactionManagement + DataSourceTransactionManager + 自己起的 MySQL 26.7.0(innodb);“更近的赢”是根据四个例子的结果下的结论,没有读源码
    • 没有验证:@Transactional 放在类上与方法上的合并规则;提交本身失败(例如延迟的约束检查)时的行为;JTA;rollbackFor 与自定义 TransactionAttribute 的组合;Spring 的 DefaultTransactionAttribute 与 RuleBasedTransactionAttribute 内部细节
    京ICP备2026067502号-1京公网安备11010502063487号