事務是確保資料一致性的基石。從資料庫 ACID 原則到 Java 應用層的事務控制,本文系統梳理 Java 事務的本質、三大類型差異與選型指南。
| 特性 | 英文 | 核心含義 |
|---|---|---|
| 原子性 | Atomicity | 事務為不可分割單元,要麼全成功,要麼全失敗回滾 |
| 一致性 | Consistency | 事務前後資料完整性約束保持一致(如轉帳總金額不變) |
| 隔離性 | Isolation | 並發事務互不干擾,透過鎖/MVCC 實現 |
| 持久性 | Durability | 提交後永久生效,即使系統崩潰(WAL/Redo Log 保證) |
Java 應用透過 JDBC 操作資料庫,事務控制(commit/rollback)從 SQL 指令轉移到 Java 程式碼中。所謂「Java 事務」,本質是 應用層對資料庫事務的封裝與協調。
經典案例:銀行轉帳
// 業務邏輯:A 轉 1000 給 B update account set balance = balance - 1000 where id = 'A'; // 步驟 1 update account set balance = balance + 1000 where id = 'B'; // 步驟 2 // 若步驟 1 成功、步驟 2 失敗(網路中斷) // 無事務:A 扣款成功,B 未收款 → 資料不一致 // 有事務:整體回滾,A 餘額恢復 → 保證一致性
Connection conn = dataSource.getConnection();
try {
conn.setAutoCommit(false); // 關閉自動提交
// 業務操作
dao.updateAccountA();
dao.updateAccountB();
conn.commit(); // 統一提交
} catch (Exception e) {
conn.rollback(); // 異常回滾
} finally {
conn.close();
}
| 特性 | 說明 |
|---|---|
| 適用範圍 | 單一資料庫連線 |
| API | Connection.setAutoCommit/commit/rollback |
| 優點 | 簡單輕量、效能最好、無額外依賴 |
| 缺點 | 無法跨多個資料庫/資料源、無法跨多個 DAO 方法協調 |
核心介面:UserTransaction、TransactionManager、XADataSource、XAResource
// JTA 程式化事務範例
@Resource UserTransaction utx;
public void transfer() {
try {
utx.begin();
// 可操作多個資料源
dao1.updateAccountA(); // 資料庫 1
dao2.updateAccountB(); // 資料庫 2
utx.commit();
} catch (Exception e) {
utx.rollback();
}
}
| 特性 | 說明 |
|---|---|
| 適用範圍 | 跨多個資料庫、跨多個資源管理器(JMS、MQ 等) |
| 核心協議 | XA 協議(兩階段提交 2PC) |
| 驅動要求 | JDBC 驅動需實作 XADataSource/XAConnection/XAResource |
| 優點 | 支援分散式事務、資源管理器無關 |
| 缺點 | 效能開銷大(2PC 同步阻塞)、配置複雜、需應用伺服器支援 |
EJB 容器提供的宣告式事務管理,基於 JTA 實現。
@Stateless
public class AccountService {
@Resource SessionContext ctx;
@TransactionAttribute(TransactionAttributeType.REQUIRED)
public void transfer(Account from, Account to, BigDecimal amount) {
// 容器自動管理事務邊界
dao.update(from);
dao.update(to);
// 異常拋出時容器自動回滾
}
}
| 特性 | 說明 |
|---|---|
| 適用範圍 | EJB 環境、Java EE 應用伺服器 |
| 配置方式 | Annotation 或 XML 宣告事務屬性 |
| 傳播行為 | REQUIRED、REQUIRES_NEW、SUPPORTS、NOT_SUPPORTED 等 |
| 優點 | 業務程式碼零侵入、容器負責複雜協調 |
| 缺點 | 綁定 Java EE 容器、較重、現代 Spring 應用較少直接使用 |
Spring 提供統一事務抽象,支援上述三種底層事務類型:
@Service
@Transactional(rollbackFor = Exception.class) // 類別層級預設
public class AccountService {
@Autowired AccountDao accountDao;
@Transactional(propagation = Propagation.REQUIRES_NEW) // 方法層級覆蓋
public void transfer(String fromId, String toId, BigDecimal amount) {
accountDao.debit(fromId, amount);
accountDao.credit(toId, amount);
}
@Transactional(readOnly = true) // 唯讀優化
public BigDecimal getBalance(String id) {
return accountDao.selectBalance(id);
}
}
| 傳播類型 | 行為 | 適用場景 |
|---|---|---|
| REQUIRED | 有事務加入,無則建立(預設) | 通用業務 |
| REQUIRES_NEW | 掛起現有事務,建立新事務 | 獨立日誌、審計 |
| NESTED | 巢狀事務(需資料庫支援 Savepoint) | 部分回滾 |
| SUPPORTS | 有事務加入,無則非事務執行 | 查詢操作 |
| NOT_SUPPORTED | 掛起事務,非事務執行 | 非關鍵操作 |
| NEVER | 不支援事務,有則拋異常 | 嚴禁事務場景 |
| MANDATORY | 必須在現有事務中執行,無則拋異常 | 強制事務上下文 |
@Transactional(isolation = Isolation.READ_COMMITTED) // 預設資料庫隔離級別
public void method() { ... }
// 五大隔離級別:DEFAULT、READ_UNCOMMITTED、READ_COMMITTED、REPEATABLE_READ、SERIALIZABLE
| 場景 | 推薦方案 | 理由 |
|---|---|---|
| 單一資料庫、簡單 CRUD | Spring @Transactional (JDBC 事務) | 輕量、效能最佳、配置簡單 |
| 微服務單體內多資料源 | Spring @Transactional + JTA (Atomikos/Bitronix) | 本地分散式事務 |
| 跨微服務分散式事務 | Seata、TCC、Saga、本地訊息表 | 避免 2PC 阻塞,追求最終一致性 |
| 高併發、強一致性要求 | 資料庫樂觀鎖/悲觀鎖 + 本地事務 | 避免分散式事務複雜度 |
| 傳統 Java EE / EJB 專案 | CMT (Container Managed Transaction) | 容器原生支援 |
@Transactional(readOnly = true),Hibernate 可跳過髒檢查、資料庫可優化執行計畫Java 事務管理的核心在於:理解 ACID 本質、識別業務邊界、選擇合適的事務類型、正確配置傳播與隔離行為。現代應用以 Spring 宣告式事務為主,複雜分散式場景轉向最終一致性方案,避免重型 JTA 兩階段提交帶來的效能與可用性代價。