首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java Transaction API (JTA) 核心機制與分散式事務實戰詳解

     編輯:Java 相關知識

JTA (Java Transaction API) 讓應用程式能執行分散式事務——在兩個或多個網路資源上原子性地存取與更新資料。JDBC 驅動的 JTA 支援極大增強了資料存取能力。本文提供 JTA 高層概述、分散式事務處理 (DTP) 模型組件、XAResource 介面實作與程式碼範例。

分散式事務處理 (DTP) 模型五大組件

  1. 應用程式:最終用戶存取點,發送請求至資料庫
  2. 應用程式伺服器:處理大部分業務操作,分擔客戶端負載
  3. 事務管理器:協調多資源管理器,決定全域提交或回滾
  4. 資源適配器:通訊管道/請求轉譯器 (如 JDBC 驅動)
  5. 資源管理器:實際資料管理系統 (如 Oracle、SQL Server)

從本地事務到分散式事務

本地事務:應用 → JDBC 驅動 → 單一資料庫。
引入應用伺服器:增加操作層。
分散式事務:跨多個資源管理器 (多資料庫/多實例),需事務管理器協調,採用兩階段提交協定 (2PC)。

兩階段提交協定 (2PC)

  1. 準備階段:事務管理器詢問所有資源管理器是否可提交。任一無法提交 → 該分支回滾,資料不變。
  2. 提交階段:若所有資源管理器均回應可提交 → 事務管理器發送全域提交;否則發送全域回滾。

JTA 三大標準介面

介面套件角色使用者
UserTransactionjavax.transaction應用程式以程式化方式控制事務邊界應用開發者
TransactionManagerjavax.transaction應用伺服器代表受管應用控制事務範圍應用伺服器廠商
XAResourcejavax.transaction.xaX/Open XA 協定的 Java 映射,資源管理器參與事務的介面JDBC 驅動開發者

⚠️ 應用程式碼禁忌:在分散式事務範圍內,絕不可呼叫 Connection.commit()、rollback()、setAutoCommit(true),否則破壞事務基礎結構管理。

JDBC 驅動與 XAResource 實作範例

前置條件:驅動需實作 JDBC 2.0 Optional Package (XADataSource, XAConnection) 與 JTA XAResource。應用伺服器需提供 DataSource 實作與連線池。

Xid 實作 (識別事務分支)

public class MyXid implements Xid {
    protected int formatId; protected byte[] gtrid, bqual;
    public MyXid() {}
    public MyXid(int formatId, byte[] gtrid, byte[] bqual) {
        this.formatId = formatId; this.gtrid = gtrid; this.bqual = bqual;
    }
    public int getFormatId() { return formatId; }
    public byte[] getGlobalTransactionId() { return gtrid; }
    public byte[] getBranchQualifier() { return bqual; }
}
  • formatId:0 表示 OSI CCR 命名;>0 自定義格式;-1 無效
  • gtrid (全域事務 ID) + bqual (分支修飾詞) 各 64 bytes,需全域唯一

範例 1:兩階段提交單一事務分支

XADataSource xaDS = getDataSource();
XAConnection xaCon = xaDS.getXAConnection("user", "pass");
XAResource xaRes = xaCon.getXAResource();
Connection con = xaCon.getConnection();
Statement stmt = con.createStatement();
Xid xid = new MyXid(100, new byte[]{0x01}, new byte[]{0x02});

try {
    xaRes.start(xid, XAResource.TMNOFLAGS);
    stmt.executeUpdate("INSERT INTO test_table VALUES (100)");
    xaRes.end(xid, XAResource.TMSUCCESS);
    int ret = xaRes.prepare(xid);
    if (ret == XAResource.XA_OK) xaRes.commit(xid, false);
} catch (XAException e) { e.printStackTrace(); }
finally { stmt.close(); con.close(); xaCon.close(); }

範例 2:回滾流程

// ... start/end 同範例1 ...
int ret = xaRes.prepare(xid);
if (ret == XAResource.XA_OK) xaRes.rollback(xid);

範例 3:暫停/恢復事務分支 (穿插本地事務)

xaRes.start(xid, XAResource.TMNOFLAGS);
stmt.executeUpdate("INSERT INTO test_table VALUES (100)");
xaRes.end(xid, XAResource.TMSUSPEND);  // 暫停分散式事務
stmt.executeUpdate("INSERT INTO test_table2 VALUES (111)"); // 本地事務,不受 XA 影響
xaRes.start(xid, XAResource.TMRESUME); // 恢復分散式事務
stmt.executeUpdate("INSERT INTO test_table VALUES (200)");
xaRes.end(xid, XAResource.TMSUCCESS);
xaRes.prepare(xid);
xaRes.rollback(xid);

範例 4:單一 XAResource 參與多個不同全域事務

Xid xid1 = new MyXid(100, new byte[]{0x01}, new byte[]{0x02});
Xid xid2 = new MyXid(100, new byte[]{0x11}, new byte[]{0x22});
xaRes.start(xid1, XAResource.TMNOFLAGS);
stmt.executeUpdate("INSERT INTO test_table1 VALUES (100)");
xaRes.end(xid1, XAResource.TMSUCCESS);
xaRes.start(xid2, XAResource.TMNOFLAGS);
xaRes.prepare(xid1);
xaRes.commit(xid2, false); // 先提交 xid2
stmt.executeUpdate("INSERT INTO test_table2 VALUES (200)");
xaRes.end(xid2, XAResource.TMSUCCESS);
xaRes.prepare(xid2);
xaRes.rollback(xid2);

範例 5:多連線共享同一事務分支 (效能優化)

// 連線 1
XAConnection xaCon1 = xaDS.getXAConnection(...);
XAResource xaRes1 = xaCon1.getXAResource();
Xid xid1 = new MyXid(...);
xaRes1.start(xid1, XAResource.TMNOFLAGS);
stmt1.executeUpdate("INSERT INTO test_table1 VALUES (100)");
xaRes1.end(xid1, XAResource.TMSUCCESS);

// 連線 2 - 檢查是否同一資源管理器
XAConnection xaCon2 = xaDS.getXAConnection(...);
XAResource xaRes2 = xaCon2.getXAResource();
if (xaRes2.isSameRM(xaRes1)) { // 同一 RM → 加入既有分支
    xaRes2.start(xid1, XAResource.TMJOIN);
    stmt2.executeUpdate("INSERT INTO test_table2 VALUES (100)");
    xaRes2.end(xid1, XAResource.TMSUCCESS);
} else { /* 建立新分支 */ }

xaRes1.prepare(xid1);
xaRes1.commit(xid1, false);

範例 6:錯誤恢復 (掃描 prepared/heuristic 分支)

Xid[] xids = xaRes.recover(XAResource.TMSTARTRSCAN | XAResource.TMENDRSCAN);
for (Xid xid : xids) {
    try { xaRes.rollback(xid); }
    catch (XAException ex) {
        try { xaRes.forget(xid); } // 放棄無法回滾的分支
        catch (XAException ex1) { System.out.println("rollback/forget failed: " + ex1.errorCode); }
    }
}

關鍵需求總結

  • JDBC 驅動:實作 XADataSource, XAConnection, XAResource
  • 應用伺服器:提供 DataSource、連線池、整合事務管理器
  • 事務管理器:實作 2PC 協調邏輯

企業級應用建議使用成熟應用伺服器 (WebLogic, WebSphere, JBoss 等) 內建事務管理器,而非自行實作應用層事務協調邏輯。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved