事務是企業級應用數據一致性的基石。從單庫本地事務到分布式全局事務,Java 提供了 JDBC、JTA、容器事務三層事務管理體系。本文將系統梳理事務核心原理(ACID)、三種事務類型差異、JDBC 事務深度解析、隔離級別與並發異常、保存點機制,幫助開發者建立完整的事務知識體系。
事務本質:一組原子操作單元,要麼全部成功提交,要麼全部失敗回滾。
從數據庫事務到 Java 事務:Java 應用通過 JDBC 操作數據庫,事務控制隨之上移至 Java 代碼層。所謂「Java 事務」實際上是 Java 代碼中對數據庫事務邊界的程序化控制。
經典案例:銀行轉賬。A 轉 1000 元給 B,需扣減 A 賬戶、增加 B 賬戶。若網絡中斷導致 B 增加失敗,必須回滾 A 的扣減,否則資金憑空消失。這就是事務的「原子性」保障。
| 特性 | 英文 | 核心含義 |
|---|---|---|
| 原子性 | Atomicity | 事務不可分割,全部執行或全部不執行 |
| 一致性 | Consistency | 事務前後數據滿足完整性約束(主鍵、外鍵、檢查約束等) |
| 隔離性 | Isolation | 並發事務互不干擾,中間狀態對其他事務不可見 |
| 持久性 | Durability | 提交後的修改永久保存,掉電不丟失 |
| 維度 | JDBC 事務 | JTA 事務 | 容器事務 (CMT) |
|---|---|---|---|
| 核心 API | java.sql.Connection | javax.transaction.UserTransaction | EJB @TransactionAttribute / Spring @Transactional |
| 事務邊界 | 單個 Connection | 跨多個資源 (多庫、MQ、緩存) | 方法級聲明式 |
| 適用場景 | 單數據源、簡單業務 | 分布式事務、多資源協調 | EJB/Spring 企業級應用 |
| 複雜度 | 低,手動控制 | 高,需 XADataSource、事務管理器 | 低,配置式,容器托管 |
| 代碼侵入性 | 強,業務代碼混雜事務代碼 | 中,需 JNDI 查找 UserTransaction | 弱,註解聲明分離關注點 |
| 回滾控制 | conn.rollback() | utx.rollback() | 拋出 RuntimeException 自動回滾 |
Connection conn = dataSource.getConnection();
try {
conn.setAutoCommit(false); // 關鍵:關閉自動提交
// 業務操作 1
updateAccount(conn, fromAccount, -1000);
// 業務操作 2
updateAccount(conn, toAccount, +1000);
conn.commit(); // 統一提交
} catch (Exception e) {
conn.rollback(); // 任一失敗全盤回滾
throw e;
} finally {
conn.setAutoCommit(true); // 恢復默認
conn.close();
}
局限:綁定單個 Connection,無法跨數據庫、跨 Connection 協調。
核心組件:
UserTransaction:應用控制事務邊界TransactionManager:事務管理器實現協調邏輯XAResource:資源管理器適配器(需 JDBC 驅動支持 XA)XADataSource:生產 XAConnection 的工廠// JNDI 獲取 UserTransaction
UserTransaction utx = (UserTransaction) new InitialContext().lookup("java:comp/UserTransaction");
try {
utx.begin();
// 操作數據庫 1
Connection conn1 = ds1.getConnection(); // 必須是 XADataSource
// 操作數據庫 2
Connection conn2 = ds2.getConnection();
// 業務邏輯...
utx.commit(); // 兩階段提交協調
} catch (Exception e) {
utx.rollback();
throw e;
}
關鍵約束:XA 連接不支持 setAutoCommit、commit()、rollback(),必須通過 UserTransaction 控制。
// EJB 容器事務
@Stateless
public class TransferService {
@Resource SessionContext ctx;
@TransactionAttribute(TransactionAttributeType.REQUIRED)
public void transfer(Account from, Account to, BigDecimal amount) {
// 容器自動開啟事務
accountDAO.debit(from, amount);
accountDAO.credit(to, amount);
// 容器自動提交/回滾
}
@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public void auditLog(String msg) { ... } // 獨立事務
}
Spring @Transactional 實現了無 EJB 容器的聲明式事務,底層基於 AOP + PlatformTransactionManager,支持 JDBC、JTA、Hibernate 等多種事務實現。
| 模式 | 特點 | 適用場景 |
|---|---|---|
| 自動提交 (默認) | 每條 SQL 隱式 commit | 只讀查詢、單條更新 |
| 顯式事務 | begin transaction ... commit/rollback | 多條 SQL 邏輯單元 |
| 隱式事務 | 隱式開始,顯式結束 | SQL Server 特有,鏈式事務 |
| 問題 | 現象 | 例子 |
|---|---|---|
| 髒讀 | 讀到未提交數據 | A update 未 commit,B select 讀到;A rollback,B 數據無效 |
| 不可重複讀 | 同一查詢前後結果不一致 | B select,A update commit,B 再 select 發現數據變了 |
| 幻讀 | 同一條件查詢行數變化 | B select 某範圍,A insert/delete commit,B 再 select 多/少行 |
| 隔離級別 | 常量 | 髒讀 | 不可重複讀 | 幻讀 | 性能 |
|---|---|---|---|---|---|
| 無事務支持 | TRANSACTION_NONE | - | - | - | - |
| 讀未提交 | READ_UNCOMMITTED | ✓ | ✓ | ✓ | 最高 |
| 讀已提交 | READ_COMMITTED | ✗ | ✓ | ✓ | 高 |
| 可重複讀 | REPEATABLE_READ | ✗ | ✗ | ✓ | 中 |
| 可串行化 | SERIALIZABLE | ✗ | ✗ | ✗ | 最低 |
默認級別:Oracle/PostgreSQL/SQL Server 為 READ_COMMITTED;MySQL InnoDB 為 REPEATABLE_READ。
// 設置隔離級別 conn.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED); // 判斷驅動支持 DatabaseMetaData meta = conn.getMetaData(); boolean supports = meta.supportsTransactionIsolationLevel(Connection.TRANSACTION_SERIALIZABLE);
Connection conn = dataSource.getConnection();
conn.setAutoCommit(false);
try {
updateA(conn);
Savepoint sp1 = conn.setSavepoint("SP1"); // 設置保存點
updateB(conn);
if (checkCondition()) {
conn.rollback(sp1); // 回滾到 SP1,updateA 保留
}
updateC(conn);
conn.commit();
} catch (Exception e) {
conn.rollback();
} finally {
conn.releaseSavepoint(sp1); // 釋放保存點
conn.close();
}
驅動支持檢查:DatabaseMetaData.supportsSavepoints()
| 傳播類型 | 行為 | 典型場景 |
|---|---|---|
| REQUIRED (默認) | 有事務加入,無則新建 | 通用業務方法 |
| REQUIRES_NEW | 掛起當前事務,新建獨立事務 | 日誌記錄、審計、補償操作 |
| NESTED | 嵌套事務,基於保存點 | 部分回滾場景 |
| SUPPORTS | 有事務加入,無則非事務運行 | 只讀查詢 |
| NOT_SUPPORTED | 掛起事務,非事務運行 | 非關鍵輔助操作 |
| NEVER | 非事務運行,有事務拋異常 | 強制非事務場景 |
| MANDATORY | 必須在事務中運行,無則拋異常 | 核心業務強制事務 |
@Transactional(rollbackFor = Exception.class) 確保受檢異常也回滾@Transactional(readOnly = true) 釋放鎖、優化查詢計劃Java 事務管理從 JDBC 手動控制、JTA 分布式協調、到容器聲明式托管,體現了「關注點分離」與「複雜度封裝」的演進軌跡。理解 ACID 本質、掌握隔離級別權衡、熟悉傳播行為語義、選對分布式事務模式,是構建高可靠、高性能數據密集型應用的核心童子功。在微服務與雲原生時代,最終一致性 成為主流選擇,但本地事務的 ACID 保障依然是每個服務內部數據正確性的基石——沒有穩固的本地事務,就沒有可靠的分布式事務。