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

Java 資料庫事務處理實戰:JDBC 本地事務與 JTA 分散式事務對比

     編輯:Java 程式設計入門

事務基礎回顧:ACID 原則

  • 原子性:事務內所有操作要么全成功,要么全失敗回滾。
  • 一致性:事務完成後,資料庫從一個一致性狀態轉移到另一個一致性狀態 (如轉帳雙方金額總和不變)。
  • 隔離性:並發事務間互不干擾,標準隔離級別:Read Uncommitted、Read Committed、Repeatable Read、Serializable。
  • 持久性:提交後的修改永久生效,不因系統故障丟失。

案例場景:規章制度三表聯動刪除

主表 bylaw (ID, 標題, 作者...);子表 bylaw_content (正文) 、bylaw_affix (附件) 皆以 bylaw_id 關聯。刪除一筆主記錄需同步刪除三表,否則產生孤兒資料。

方案一:JavaBean + JDBC 本地事務


public int deleteBylaw(int id) {
    DataSource ds = ...; // 連接池
    Connection conn = null;
    try {
        conn = ds.getConnection();
        conn.setAutoCommit(false); // 關閉自動提交,開啟事務

        try (PreparedStatement ps1 = conn.prepareStatement("DELETE FROM bylaw WHERE ID=?")) {
            ps1.setInt(1, id);
            ps1.executeUpdate();
        }
        try (PreparedStatement ps2 = conn.prepareStatement("DELETE FROM bylaw_content WHERE bylaw_id=?")) {
            ps2.setInt(1, id);
            ps2.executeUpdate();
        }
        try (PreparedStatement ps3 = conn.prepareStatement("DELETE FROM bylaw_affix WHERE bylaw_id=?")) {
            ps3.setInt(1, id);
            ps3.executeUpdate();
        }

        conn.commit(); // 全部成功提交
        return 1;
    } catch (SQLException e) {
        if (conn != null) try { conn.rollback(); } catch (SQLException ignored) {}
        log.error("刪除失敗,已回滾", e);
        return -1;
    } finally {
        if (conn != null) try { conn.setAutoCommit(true); conn.close(); } catch (SQLException ignored) {}
    }
}

適用場景:單一資料來源、單一 JVM 內的多條 SQL 原子操作。缺點:無法跨多個資料庫、跨 JVM、或整合 JMS 訊息發送等非資料庫資源。

方案二:Session Bean + JTA (Java Transaction API) 分散式事務


@Stateless
@TransactionManagement(TransactionManagementType.CONTAINER) // CMT 容器管理事務
public class BylawServiceBean implements BylawService {

    @Resource DataSource ds;
    @Resource SessionContext ctx; // 注入 SessionContext 取得 UserTransaction

    public int deleteBylaw(int id) {
        UserTransaction utx = ctx.getUserTransaction();
        Connection conn = null;
        try {
            utx.begin(); // 開啟 JTA 事務

            conn = ds.getConnection();
            // 同樣的三條 DELETE,此處省略 PreparedStatement 代碼
            // ...

            utx.commit(); // 提交分散式事務
            return 1;
        } catch (Exception e) {
            try { utx.rollback(); } catch (Exception ignored) {}
            log.error("JTA 事務回滾", e);
            return -1;
        } finally {
            if (conn != null) try { conn.close(); } catch (SQLException ignored) {}
        }
    }
}

JTA 關鍵介面:

  • UserTransaction:應用程式層控制事務邊界 (begin/commit/rollback)。
  • TransactionManager:容器/應用伺服器內部管理事務上下文、掛起/恢復。
  • Transaction:代表具體事務實例,可註冊同步回調、查詢狀態。

適用場景:跨多個資料源 (Oracle + MySQL)、資料庫 + JMS、多個 EJB 模組、微服務協同 (配合 JTS/OTS)。

兩種模式對比表

最佳實踐建議

  1. 優先使用聲明式事務 (CMT):@Transactional (Spring) 或 @TransactionAttribute (EJB),減少樣板代碼與漏寫風險。
  2. 事務邊界盡量縮小:只包含必要的資料庫操作,避免在事務中進行網路呼叫、複雜運算、檔案 I/O,降低鎖持有時間與死鎖風險。
  3. 隔離級別按需設定:預設 Read Committed 多數足夠;報表查詢可用 Read Uncommitted;庫存扣減等需 Repeatable Read/Serializable。
  4. 異常分類決定回滾:預設僅 RuntimeException / Error 觸發回滾;檢查型異常需 @Transactional(rollbackFor = BizException.class) 明確指定。
  5. 避免巢狀事務陷阱:JDBC 不支援巢狀;JTA 僅支援扁平化,內層 begin() 會拋 NotSupportedException。若需邏輯巢狀,改用 Savepoint (JDBC) 或 TransactionSynchronization (JTA)。
  6. 連接池必須支援 XA:使用 JTA 時,DataSource 必須是 XADataSource 實作 (如 oracle.jdbc.xa.client.OracleXADataSource),否則兩階段提交會失敗。

結語

JDBC 本地事務輕量高效,適合單體應用單一資料庫;JTA 分散式事務功能強大,支援跨資源原子性,但引入 2PC 開銷與基礎設施複雜度。架構選型時遵循「簡單原則」:能用本地事務解決的,不上分散式事務;必須跨資源時,優先考慮事件驅動/最終一致性 (Saga、Outbox Pattern) 替代強一致性 JTA,降低系統耦合與延遲。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved
維度JDBC 本地事務JTA 分散式事務
資料源數量單一多個 (XA 支援)
部署環境任意 (含 Servlet 容器)需支援 JTA 的應用伺服器
效能高 (無協調器開銷)較低 (兩階段提交 2PC)
程式設計模型手動控制 Connection聲明式 (CMT) 或程式化 (BMT)
非資料庫資源不支援支援 (JCA、JMS)