Java 事務管理總結:JDBC 本地事務、JTA 分布式事務、Spring 事務抽象與選型實戰指南
事務是企業級應用數據一致性的基石。本文系統總結 JDBC 本地事務、JTA/XA 分布式事務、容器事務(CMT)、Spring 事務抽象 四大體系的原理、API、隔離級別、傳播行為、優缺點對比與選型決策樹,並附贈 JDBC 事務核心代碼模板、Spring 声明式事務最佳實踐,作為架構師與開發者的桌面參考手冊。
一、事務核心屬性(ACID)速記
- A - 原子性:單位不可分,全成全敗。
- C - 一致性:約束前後不變,數據完整。
- I - 隔離性:併發互不干擾,靠鎖/MVCC 實現。
- D - 持久性:提交即永久,掉電不丟,靠 WAL/Redo Log。
二、三大 Java 事務體系對比總覽
| 維度 |
JDBC 本地事務 |
JTA 分布式事務 |
容器事務 (CMT/EJB) |
| 核心 API | Connection.setAutoCommit/commit/rollback | UserTransaction、TransactionManager、XAResource | @TransactionAttribute / ejb-jar.xml |
| 事務範圍 | 單個 Connection / 單數據庫 | 多 XAResource / 多數據庫/消息隊列 | EJB 方法級,容器管理 |
| 實現複雜度 | 低,手動控制 | 高,需 XA 驅動、TM、2PC 協議 | 中,聲明式配置,但綁定 EJB 容器 |
| 性能開銷 | 最小(單階段提交) | 大(2PC、鎖持有久、網絡交互) | 中等(容器攔截開銷) |
| 適用場景 | 單庫應用、簡單業務 | 跨庫轉賬、訂單-庫存強一致 | 傳統 Java EE 單體、EJB 架構 |
| 現代地位 | 基石,Spring 管理底層 | 核心,Spring JtaTransactionManager 封裝 | 已淘汰,EJB 幾乎不再使用 |
三、JDBC 本地事務深度實戰
1. 核心代碼模板(生產級)
Connection conn = null;
PreparedStatement ps = null;
try {
conn = dataSource.getConnection();
conn.setAutoCommit(false); // 關鍵:開啟手動事務
ps = conn.prepareStatement("UPDATE account SET balance=balance-? WHERE id=?");
ps.setInt(1, 500); ps.setString(2, "A");
ps.executeUpdate();
ps = conn.prepareStatement("UPDATE account SET balance=balance+? WHERE id=?");
ps.setInt(1, 500); ps.setString(2, "B");
ps.executeUpdate();
conn.commit(); // 統一提交
} catch (Exception e) {
if (conn != null) try { conn.rollback(); } catch (SQLException ignore) {}
throw new RuntimeException("轉賬失敗", e); // 統一異常轉換
} finally {
closeQuietly(ps);
closeQuietly(conn); // 歸還連接池
}
2. 事務隔離級別與併發異常
| 隔離級別 |
常量 |
髒讀 |
不可重複讀 |
幻讀 |
實現機制 |
| READ_UNCOMMITTED | 1 | ✅ | ✅ | ✅ | 無鎖,極少用 |
| READ_COMMITTED | 2 | ❌ | ✅ | ✅ | 行鎖(寫),Oracle/PostgreSQL 默認 |
| REPEATABLE_READ | 4 | ❌ | ❌ | ✅ | 行鎖+間隙鎖,MySQL InnoDB 默認 |
| SERIALIZABLE | 8 | ❌ | ❌ | ❌ | 表鎖/範圍鎖,性能最差 |
- 髒讀:讀到未提交數據。
- 不可重複讀:同一查詢前後結果不一致(數據被修改)。
- 幻讀:同一條件前後查詢行數變化(數據被插入/刪除)。
- JDBC 設置:
conn.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED)。
3. Savepoint 保存點:細粒度回滾
Savepoint sp1 = conn.setSavepoint("SP1");
stmt.executeUpdate("INSERT ...");
Savepoint sp2 = conn.setSavepoint("SP2");
stmt.executeUpdate("UPDATE ...");
conn.rollback(sp2); // 回滾到 SP2,SP1 保留的 INSERT 生效
conn.commit();
四、JTA/XA 分布式事務核心機制
1. XA 兩階段提交 (2PC) 流程
- 準備階段:TM 發
prepare() → 各 RM 執行本地事務、寫 redo/undo log、持有鎖、返回 XA_OK。
- 提交階段:全
OK → TM 發 commit() → RM 正式提交、釋放鎖;任一失敗 → TM 發 rollback() → 全部回滾。
2. 關鍵接口
XADataSource → getXAConnection() → XAConnection → getXAResource() → XAResource(prepare/commit/rollback)。
- 普通
DataSource 無 getXAResource,不能參與 JTA。
3. 獨立 JTA 實現選型
| 產品 |
特點 |
Spring Boot 整合 |
| Atomikos | 成熟、文檔豐富、支持 Spring Boot Starter | spring-boot-starter-jta-atomikos |
| Bitronix | 輕量、嵌入式友好 | 手動配置 BitronixTransactionManager |
| Narayana | WildFly 內置、功能最全、支持 LRA/WS-AT | narayana-spring-boot-starter |
五、Spring 事務抽象:統一編程模型
1. 核心接口三劍客
PlatformTransactionManager:事務管理入口,getTransaction(Definition)、commit/status、rollback/status。
TransactionDefinition:隔離級別、傳播行為、超時、只讀等屬性定義。
TransactionStatus:運行時狀態,isNewTransaction()、setRollbackOnly()、isRollbackOnly()。
2. 實現類選擇
DataSourceTransactionManager:單 DataSource 本地事務(最常用)。
JtaTransactionManager:委托 JTA 實現(Atomikos/Narayana/容器 TM),支持分布式事務。
HibernateTransactionManager:Hibernate Session 事務(已少用)。
3. 七大傳播行為詳解
| 傳播常量 |
行為描述 |
典型場景 |
| REQUIRED | 支持當前事務,無則新建(默認) | 90% 業務方法 |
| SUPPORTS | 支持當前事務,無則非事務執行 | 只讀查詢、非關鍵輔助操作 |
| MANDATORY | 必須在事務中運行,無則拋異常 | 強制要求事務上下文的核心業務 |
| REQUIRES_NEW | 掛起當前事務,新建獨立事務 | 日志記錄、審計、獨立提交的子業務 |
| NOT_SUPPORTED | 掛起當前事務,非事務運行 | 長耗時非事務操作、外部 API 調用 |
| NEVER | 非事務運行,有事務則拋異常 | 嚴禁事務的場景 |
| NESTED | 嵌套事務(Savepoint),外層回滾內層跟隨,內層回滾外層可選 | 細粒度回滾控制,JDBC 3.0+ 支持 |
4. 聲明式事務最佳實踐(@Transactional)
@Service
@Transactional(rollbackFor = Exception.class) // 類級默認:所有受檢/非受檢異常回滾
public class AccountService {
@Autowired AccountMapper mapper;
// 方法級覆蓋:只讀優化、特定傳播、超時
@Transactional(readOnly = true, propagation = Propagation.SUPPORTS)
public Account getAccount(String id) { return mapper.selectById(id); }
@Transactional(propagation = Propagation.REQUIRES_NEW, timeout = 30)
public void logTransfer(TransferLog log) { logMapper.insert(log); }
// 核心業務:默認 REQUIRED,異常回滾
public void transfer(String from, String to, int amount) {
mapper.decreaseBalance(from, amount);
// 業務檢查拋出業務異常 → 觸發回滾
if (mapper.getBalance(from) < 0) throw new InsufficientBalanceException();
mapper.increaseBalance(to, amount);
}
}
5. 事務失效十大坑
- 方法非
public(代理攔截不到)。
- 類內部自調用(
this.method() 繞過代理)。
- 捕獲異常未重拋(
catch { log } 吞掉異常)。
- 拋出非
rollbackFor 指定異常(默認只回滾 RuntimeException/Error)。
- 數據源未配置事務管理器 / 多數據源未指定
@Transactional("txManager")。
- 使用
@Transactional 在非 Spring Bean 上(普通類、final 類、final 方法)。
- 異步線程中調用(事務上下文不傳遞)。
- 引擎不支持事務(MyISAM、MongoDB 無事務)。
- 嵌套事務未正確配置
NESTED / 數據庫不支持 Savepoint。
- 序列化隔離級別導致死鎖/超時。
六、選型決策樹:架構師視角
業務場景分析
│
├─ 單數據庫、單應用
│ └─ → Spring @Transactional + DataSourceTransactionManager (首選)
│
├─ 多數據庫 / 數據庫+MQ / 微服務強一致
│ ├─ 業務容許最終一致?
│ │ ├─ 是 → TCC / Saga / 本地消息表 / 事務消息 (RocketMQ) → 避開分布式事務
│ │ └─ 否 → 必須強一致
│ │ ├─ 同一應用跨庫 → Spring JtaTransactionManager + Atomikos/Narayana + XADataSource
│ │ └─ 微服務跨服務 → Seata AT 模式 / XA 模式 / TCC 模式
│
└─ 歷史 EJB 應用維護
→ 容器 CMT(了解即可,新項目禁用)
七、總結
Java 事務技術棧經歷了 JDBC 手動 → JTA/XA 標準 → 容器 CMT → Spring 統一抽象 → 微服務柔性事務 的演進。核心認知三點:
- 本地事務是基石:單庫場景 Spring
@Transactional 管理 DataSourceTransactionManager 最簡潔高效。
- 分布式事務是雙刃劍:XA/2PC 提供強一致但犧牲性能/可用性,**能不用盡量不用**,優先選最終一致性方案(Seata、TCC、Saga)。
- Spring 抽象屏蔽差異:同一套
@Transactional 注解,切換 PlatformTransactionManager 實現即可在本地/分布式間遷移,業務代碼零侵入。
掌握 隔離級別、傳播行為、失效坑點、選型邏輯,你就擁有了在任何 Java 架構下設計正確事務邊界的核心能力。