JTA和JDBC事務

 一般情況下,J2EE應用服務器支持JDBC事務、JTA事務、容器管理事務。這裏討論JTA和JDBC事務的區別。這2個是常用的DAO模式事務界定方式。
JDBC 事務
 JDBC 事務是用 Connection 對象控制的。JDBC Connection 接口( java.sql.Connection )提供了兩種事務模式:自動提交和手工提交。
★ 在jdbc中,事務操作缺省是自動提交。也就是說,一條對數據庫的更新表達式代表一項事務操作,操作成功後,系統將自動調用commit()來提交,否則將調用rollback()來回滾。
★  在jdbc中,可以通過調用setAutoCommit(false)來禁止自動提交。之後就可以把多個數據庫操作的表達式作爲一個事務,在操作完成後調 用commit()來進行整體提交,倘若其中一個表達式操作失敗,都不會執行到commit(),並且將產生響應的異常;此時就可以在異常捕獲時調用 rollback()進行回滾。這樣做可以保持多次更新操作後,相關數據的一致性,示例如下:

 try {

conn = 

DriverManager.getConnection    

("jdbc:oracle:thin:@host:1521:SID","username","userpwd";

       conn.setAutoCommit(false);//禁止自動提交,設置回滾點

       stmt = conn.createStatement();

stmt.executeUpdate(“alter table …”); //數據庫更新操作1

stmt.executeUpdate(“insert into table …”); //數據庫更新操作2

       conn.commit(); //事務提交

     }catch(Exception ex) {    

         ex.printStackTrace();

         try {

          conn.rollback(); //操作不成功則回滾

          }catch(Exception e) {

e.printStackTrace();

           }

}


 JDBC 事務的一個缺點是事務的範圍侷限於一個數據庫連接。一個 JDBC 事務不能跨越多個數據庫。

JTA事務

JTA(Java Transaction API) 爲 J2EE 平臺提供了分佈式事務服務。
 要用 JTA 進行事務界定,應用程序要調用 javax.transaction.UserTransaction 接口中的方法。例如:

utx.begin();
      // ...
      DataSource ds = obtainXADataSource();
      Connection conn = ds.getConnection();
      pstmt = conn.prepareStatement("UPDATE MOVIES ...");
      pstmt.setString(1, "Spinal Tap");
      pstmt.executeUpdate();
      // ...
      utx.commit();


讓我們來關注下面的話:
 “用 JTA 界定事務,那麼就需要有一個實現 javax.sql.XADataSource 、 javax.sql.XAConnection 和 javax.sql.XAResource 接口的 JDBC 驅動程序。一個實現了這些接口的驅動程序將可以參與 JTA 事務。一個 XADataSource 對象就是一個 XAConnection 對象的工廠。 XAConnection s 是參與 JTA 事務的 JDBC 連接。”
 要使用JTA事務,必須使用XADataSource來產生數據庫連接,產生的連接爲一個XA連接。
 XA連接(javax.sql.XAConnection)和非XA(java.sql.Connection)連接的區別在於:XA可以參與JTA的事務,而且不支持自動提交。
     Note:
Oracle, Sybase, DB2, SQL Server等大型數據庫才支持XA, 支持分佈事務。
My SQL 連本地都支持不好,更別說分佈事務了。
JTA方式的實現過程:
   用XADataSource產生的XAConnection它擴展了一個getXAResource()方法,事務通過這個方法把它加入到事務容器中進行 管理.對於調用者來說,根本看不到事務是如果管理的,你只要聲明開始事務,告訴容器我下面的操作要求事務參與了,最後告訴事務說到這兒可以提交或回滾了, 別的都是黑箱操作。
 在使用JTA之前,你必須首先實現一個Xid類用來標識事務(在普通情況下這將由事務管理程序來處理)。Xid包含三個元素:formatID、gtrid(全局事務標識符)和bqual(分支修飾詞標識符)。
 下面的例子說明Xid的實現:

import javax.transaction.xa.*; 
public class MyXid implements Xid 
{ 
protected int formatId; 
protected byte gtrid[]; 
protected byte 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[] getBranchQualifier() 
{ 
return bqual; 
} 

 public byte[] getGlobalTransactionId() 
{ 
return gtrid; 
} 

}


 其次,你需要創建一個你要使用的數據庫的數據源:

public DataSource getDataSource() 
throws SQLException 
{ 
SQLServerDataSource xaDS = new 
com.merant.datadirect.jdbcx.sqlserver.SQLServerDataSource(); 
xaDS.setDataSourceName("SQLServer"); 
xaDS.setServerName("server"); 
xaDS.setPortNumber(1433); 
xaDS.setSelectMethod("cursor"); 
return xaDS; 
} 

這個例子是用“兩步提交協議”來提交一個事務分支:

XADataSource xaDS; 
XAConnection xaCon; 
XAResource xaRes; 
Xid xid; 
Connection con; 
Statement stmt; 
int ret; 
xaDS = getDataSource(); 
xaCon = xaDS.getXAConnection("jdbc_user", "jdbc_password"); 
xaRes = xaCon.getXAResource(); 
con = xaCon.getConnection(); 
stmt = con.createStatement(); 
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); 
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(); 
}

 當然,實際過程中,我們不需要寫這些代碼,這些代碼是JTA最終的實現代碼。
關於“兩步提交協議”,可以參看下面的文章:
http://www.ibm.com/developerworks/cn/db2/library/techarticles/dm-0505weber/index.html

兩階段提交(Two-Phase-Commit)協議

首先,兩階段提交(Two-Phase-Commit)事務的啓動與常規的單階段提交(One-Phase-Commit)事務類似。接着,應用程序/客 戶機對該兩階段提交(Two-Phase-Commit)操作中所涉及的所有數據庫執行其修改工作。現在,在最終提交該事務之前,客戶機通知參與的數據庫準備提交(第 1 階段)。如果客戶機從數據庫收到一條“okay”,就發出命令向數據庫提交該事務(第 2 階段)。最後分佈式事務(Distributed Transaction)結束。

本文中的例子演示瞭如何在 Java 中使用 JTA 實現兩階段提交(Two-Phase-Commit)協議。在該應用程序中,如果一個事務分支報告了錯誤,您就要負責進行錯誤處理。但是“兩階段提交協議 簡介”小節中提到仍然存在一個問題,那就是如果第 2 階段中一個事務分支發生故障,該怎麼辦呢?

如果再次查看程序代碼,您可以看到在“第 1 階段”和“第 2 階段”之間有一個很小的時間間隔。在這一時間間隔中,出於某種理由,其中某一參與數據庫可能崩潰。如果發生了,我們將陷入分佈式事務已經部分提交的情形中。

假 定下列情形:在“第 1 階段”之後,您從 DB2 和 IDS 數據庫中都收到了“okay”。在下一步中,應用程序成功提交了 DB2 的事務分支。接着,應用程序通知 DB2 事務分支提交事務。現在,在應用程序可以通知 IDS 事務分支提交它這一部分之前,IDS 引擎由於斷電發生崩潰。這就是一種部分提交全局事務的情形。您現在該怎麼辦呢?

在重啓之後,DB2 和 IDS 都將嘗試恢復打開的事務分支。該引擎等待來自應用程序的提示如何做。如果應用程序沒有準備重新發送“第 2 階段”的提交,該事務分支將被引擎所啓動的試探性回滾中止。這是非常糟糕的,因爲這將使該全局事務處於不一致狀態。

一種解決方案是用一個小型應用程序連接引擎中打開的事務分支,並通知引擎提交或回滾這一打開的事務。如果您使用 IDS 作爲後端,那麼還有一個隱藏的 onmode 標誌,允許您結束打開的事務分支。(onmode -Zxid)。

在 DB2 UDB 中,您可以發出 LIST INDOUBT TRANSACTIONS 來獲得打開的 XA 事務的有關信息。您必須查看DB2 Information Center 中的描述來解決該問題。

上面描述的情形是一個很好的例子,也是使用應用程序服務器(Application Server)或事務監控器(Transaction Monitor)的理由。在使用一箇中間層服務器時,就由該服務器負責保持事情正常。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章