核心結論
Spring @Transactional 默認僅對 RuntimeException 及其子類、Error 觸發回滾;Exception(受檢異常)不回滾。
異常體系分類速覽
Throwable
├── Error // 虛擬機錯誤,不可恢復(OOM、StackOverflow)
└── Exception
├── RuntimeException // 運行時異常:空指針、越界、類轉換、算術...
│ ├── NullPointerException
│ ├── IndexOutOfBoundsException
│ └── ...
└── Checked Exception // 受檢異常:必須 try-catch 或 throws
├── IOException
├── SQLException
├── ClassNotFoundException
└── ...
默認行為驗證
@Transactional
public void methodA() {
dao.insert(...);
throw new RuntimeException("故意拋運行時異常"); // 回滾 ✅
}
@Transactional
public void methodB() throws Exception {
dao.insert(...);
throw new IOException("受檢異常"); // 不回滾 ❌
}
自定義回滾規則
1. 指定回滾異常
@Transactional(rollbackFor = Exception.class) // 所有 Exception 及子類回滾
@Transactional(rollbackFor = {IOException.class, MyBizException.class}) // 指定類型
2. 指定不回滾異常
@Transactional(noRollbackFor = RuntimeException.class) // 運行時異常也不回滾(極少用)
3. XML 配置方式
<tx:method name="*" rollback-for="Exception" no-rollback-for="MyIgnoreException"/>
實戰建議:自定義異常體系設計
// 基礎業務異常 → 繼承 RuntimeException,享受默認回滾
public class BizException extends RuntimeException {
private final ErrorCode code;
public BizException(ErrorCode code, String msg) { super(msg); this.code = code; }
public BizException(ErrorCode code, String msg, Throwable cause) { super(msg, cause); this.code = code; }
}
// 特定場景:不想回滾的業務異常(如驗證失敗、幂等重複)
public class NoRollbackBizException extends Exception { // 繼承 Exception
// ...
}
- 業務代碼拋
BizException → 自動回滾,無需配置;
- 特殊場景拋
NoRollbackBizException → 需在 @Transactional 聲明 rollbackFor = Exception.class 才會回滾,否則提交;
- 統一異常碼
ErrorCode 便於前端處理與日志追蹤。
常見誤區
- try-catch 吞掉異常:事務攔截器捕獲不到異常,不會回滾 → catch 後必須重新拋出;
- 多線程調用:@Transactional 基於 ThreadLocal 綁定連接,子線程不共享事務上下文 → 需編程式事務或傳遞連接;
- 內部方法調用:同類內部調用繞過代理,事務失效 → 注入自身 Bean 或拆分類;
- Propagation.NESTED:僅 DataSourceTransactionManager 支援,JTA 不支援。
總結決策表
| 場景 | 推薦策略 |
| 通用業務方法 | 拋 BizException (RuntimeException),無需配置 |
| 需回滾受檢異常 | @Transactional(rollbackFor = Exception.class) |
| 特定異常不回滾 | 自定義 Exception 子類 + noRollbackFor |
| 批量導入部分失敗不回滾整體 | 編程式事務 TransactionTemplate 手動控制 |