Spring @Transactional 必须加 rollbackFor = Exception.class 的原因详解
·
Spring @Transactional 必须加 rollbackFor = Exception.class 的原因详解
一、先说结论
Spring 的
@Transactional默认只对 RuntimeException 和 Error 回滚,不会对受检异常(Checked Exception,如IOException、SQLException)回滚。因此阿里巴巴开发规范要求必须显式指定rollbackFor = Exception.class,防止部分操作成功、部分失败导致的数据不一致问题。
二、默认回滚行为 vs 显式指定回滚
不加 rollbackFor(默认行为)
| 异常类型 | 是否回滚 |
|---|---|
RuntimeException 及其子类(如 NullPointerException) |
✅ 回滚 |
Error 及其子类(如 OutOfMemoryError) |
✅ 回滚 |
受检异常(如 IOException、SQLException) |
❌ 不回滚 |
加 rollbackFor = Exception.class
| 异常类型 | 是否回滚 |
|---|---|
RuntimeException 及其子类 |
✅ 回滚 |
Error 及其子类 |
✅ 回滚 |
受检异常(如 IOException、SQLException) |
✅ 回滚 |
简单来说,加了 rollbackFor = Exception.class 后,所有 Exception 及其子类都会触发回滚,包括运行时异常和受检异常。 |
三、一个真实场景:不加 rollbackFor 的后果
@Transactional
public void createAircraft() {
mapper.insert(aircraft); // 第1步:插入数据库,成功
importDataFromFile(); // 第2步:读取文件,抛出 IOException
}
不加 rollbackFor: 第2步抛出 IOException(受检异常),Spring 认为不需要回滚,第1步的数据已经落库,导致数据库中存在一条不完整的数据。
加了 rollbackFor = Exception.class: 第2步抛出 IOException,Spring 触发事务回滚,第1步的插入操作也会被撤销,数据库保持一致。
四、什么是 IOException?
IOException 是 Java 中的受检异常(Checked Exception),表示输入/输出操作失败或中断时抛出的异常。
常见触发场景:
// 1. 读取不存在的文件
FileInputStream fis = new FileInputStream("不存在的文件.txt");
// 2. 网络连接中断
InputStream is = new URL("http://example.com").openStream();
// 3. 写入文件失败(磁盘满、权限不足)
FileOutputStream fos = new FileOutputStream("/readonly/test.txt");
fos.write(data);
注意:编译器会强制要求你处理受检异常(try-catch 或 throws 声明),否则代码无法编译。这也是它叫"受检异常"的原因。
五、受检异常 vs 非受检异常
继承关系
Object
└── Throwable
├── Error ← JVM 级别错误,通常不处理
└── Exception
├── RuntimeException ← 非受检异常,编译器不强制处理
│ ├── NullPointerException
│ ├── IllegalArgumentException
│ └── ArrayIndexOutOfBoundsException
└── IOException ← 受检异常,编译器强制处理
└── SQLException ← 受检异常,编译器强制处理
└── ClassNotFoundException ← 受检异常,编译器强制处理
核心区别
| 受检异常(Checked) | 非受检异常(Unchecked) | |
|---|---|---|
| 继承 | Exception 的非 RuntimeException 子类 |
RuntimeException 及其子类 |
| 编译器 | 强制处理(try-catch 或 throws) | 不强制处理 |
| 常见示例 | IOException、SQLException、ClassNotFoundException |
NullPointerException、IllegalArgumentException |
| @Transactional 默认回滚 | ❌ 不回滚 | ✅ 回滚 |
六、最佳实践
✅ 推荐:始终显式指定 rollbackFor
@Transactional(rollbackFor = Exception.class)
public void createAircraft() {
mapper.insert(aircraft);
importDataFromFile();
}
❌ 不推荐:依赖默认行为
@Transactional
public void createAircraft() {
mapper.insert(aircraft);
importDataFromFile(); // 如果抛出 IOException,事务不会回滚!
}
七、总结
| 对比项 | 不加 rollbackFor |
加 rollbackFor = Exception.class |
|---|---|---|
RuntimeException |
回滚 | 回滚 |
Error |
回滚 | 回滚 |
IOException(受检) |
不回滚 ⚠️ | 回滚 ✅ |
SQLException(受检) |
不回滚 ⚠️ | 回滚 ✅ |
| 阿里巴巴规范 | ❌ 违规 | ✅ 符合 |
一句话总结: 只要你的业务方法中涉及文件操作、网络请求、第三方接口调用等可能抛出受检异常的操作,就必须加 rollbackFor = Exception.class,否则一旦受检异常发生,事务不会回滚,将导致严重的数据不一致问题。养成习惯,所有 @Transactional 都显式指定 rollbackFor,是最安全的做法。 |
更多推荐




所有评论(0)