一般情況下,J2EE應(yīng)用服務(wù)器支持JDBC事務(wù)、JTA事務(wù)、容器管理事務(wù)。這里討論JTA和JDBC事務(wù)的區(qū)別。這2個是常用的DAO模式事務(wù)界定方式。
 JDBC 事務(wù)
 JDBC 事務(wù)是用 Connection 對象控制的。JDBC Connection 接口( java.sql.Connection )提供了兩種事務(wù)模式:自動提交和手工提交。
★ 在jdbc中,事務(wù)操作缺省是自動提交。也就是說,一條對數(shù)據(jù)庫的更新表達式代表一項事務(wù)操作,操作成功后,系統(tǒng)將自動調(diào)用commit()來提交,否則將調(diào)用rollback()來回滾。
★  在jdbc中,可以通過調(diào)用setAutoCommit(false)來禁止自動提交。之后就可以把多個數(shù)據(jù)庫操作的表達式作為一個事務(wù),在操作完成后調(diào) 用commit()來進行整體提交,倘若其中一個表達式操作失敗,都不會執(zhí)行到commit(),并且將產(chǎn)生響應(yīng)的異常;此時就可以在異常捕獲時調(diào)用 rollback()進行回滾。這樣做可以保持多次更新操作后,相關(guān)數(shù)據(jù)的一致性,示例如下:
try {
   conn 
= DriverManager.getConnection    
   conn.setAutoCommit(
false);//禁止自動提交,設(shè)置回滾點
   stmt = conn.createStatement();
   stmt.executeUpdate(“alter table …”); 
//數(shù)據(jù)庫更新操作1
   stmt.executeUpdate(“insert into table …”); //數(shù)據(jù)庫更新操作2
   conn.commit(); //事務(wù)提交
}
catch(Exception ex) {    
   ex.printStackTrace();
   
try {
      conn.rollback(); 
//操作不成功則回滾
   }
catch(Exception e) {
       e.printStackTrace();
   }

}


JDBC 事務(wù)的一個缺點是事務(wù)的范圍局限于一個數(shù)據(jù)庫連接。一個 JDBC 事務(wù)不能跨越多個數(shù)據(jù)庫。

 JTA事務(wù)
 JTA(Java Transaction API) 為 J2EE 平臺提供了分布式事務(wù)服務(wù)。
 要用 JTA 進行事務(wù)界定,應(yīng)用程序要調(diào)用 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 界定事務(wù),那么就需要有一個實現(xiàn) javax.sql.XADataSource 、 javax.sql.XAConnection 和 javax.sql.XAResource 接口的 JDBC 驅(qū)動程序。一個實現(xiàn)了這些接口的驅(qū)動程序?qū)⒖梢詤⑴c JTA 事務(wù)。一個 XADataSource 對象就是一個 XAConnection 對象的工廠。 XAConnection s 是參與 JTA 事務(wù)的 JDBC 連接。”
 要使用JTA事務(wù),必須使用XADataSource來產(chǎn)生數(shù)據(jù)庫連接,產(chǎn)生的連接為一個XA連接。
 XA連接(javax.sql.XAConnection)和非XA(java.sql.Connection)連接的區(qū)別在于:XA可以參與JTA的事務(wù),而且不支持自動提交。
 Note:
Oracle, Sybase, DB2, SQL Server等大型數(shù)據(jù)庫才支持XA, 支持分布事務(wù)。 
My SQL 連本地都支持不好,更別說分布事務(wù)了。

JTA方式的實現(xiàn)過程:
   用XADataSource產(chǎn)生的XAConnection它擴展了一個getXAResource()方法,事務(wù)通過這個方法把它加入到事務(wù)容器中進行 管理.對于調(diào)用者來說,根本看不到事務(wù)是如果管理的,你只要聲明開始事務(wù),告訴容器我下面的操作要求事務(wù)參與了,最后告訴事務(wù)說到這兒可以提交或回滾了, 別的都是黑箱操作。
 在使用JTA之前,你必須首先實現(xiàn)一個Xid類用來標識事務(wù)(在普通情況下這將由事務(wù)管理程序來處理)。Xid包含三個元素:formatID、gtrid(全局事務(wù)標識符)和bqual(分支修飾詞標識符)。
 下面的例子說明Xid的實現(xiàn):
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; 
    }
 
}

其次,你需要創(chuàng)建一個你要使用的數(shù)據(jù)庫的數(shù)據(jù)源:
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; 
}
 

這個例子是用“兩步提交協(xié)議”來提交一個事務(wù)分支:

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(100new 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(); 
}



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

首先,兩階段提交(Two
-Phase-Commit)事務(wù)的啟動與常規(guī)的單階段提交(One-Phase-Commit)事務(wù)類似。接著,應(yīng)用程序/客 戶機對該兩階段提交(Two-Phase-Commit)操作中所涉及的所有數(shù)據(jù)庫執(zhí)行其修改工作。現(xiàn)在,在最終提交該事務(wù)之前,客戶機通知參與的數(shù)據(jù)庫準備提交(第 1 階段)。如果客戶機從數(shù)據(jù)庫收到一條“okay”,就發(fā)出命令向數(shù)據(jù)庫提交該事務(wù)(第 2 階段)。最后分布式事務(wù)(Distributed Transaction)結(jié)束。

在該應(yīng)用程序中,如果一個事務(wù)分支報告了錯誤,您就要負責進行錯誤處理。但是“兩階段提交協(xié)議 簡介”小節(jié)中提到仍然存在一個問題,那就是如果第 2 階段中一個事務(wù)分支發(fā)生故障,該怎么辦呢?

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

假 定下列情形:在“第 1 階段”之后,您從 DB2 和 IDS 數(shù)據(jù)庫中都收到了“okay”。在下一步中,應(yīng)用程序成功提交了 DB2 的事務(wù)分支。接著,應(yīng)用程序通知 DB2 事務(wù)分支提交事務(wù)。現(xiàn)在,在應(yīng)用程序可以通知 IDS 事務(wù)分支提交它這一部分之前,IDS 引擎由于斷電發(fā)生崩潰。這就是一種部分提交全局事務(wù)的情形。您現(xiàn)在該怎么辦呢?

在重啟之后,DB2 和 IDS 都將嘗試恢復(fù)打開的事務(wù)分支。該引擎等待來自應(yīng)用程序的提示如何做。如果應(yīng)用程序沒有準備重新發(fā)送“第 2 階段”的提交,該事務(wù)分支將被引擎所啟動的試探性回滾中止。這是非常糟糕的,因為這將使該全局事務(wù)處于不一致狀態(tài)。

一種解決方案是用一個小型應(yīng)用程序連接引擎中打開的事務(wù)分支,并通知引擎提交或回滾這一打開的事務(wù)。如果您使用 IDS 作為后端,那么還有一個隱藏的 onmode 標志,允許您結(jié)束打開的事務(wù)分支。(onmode -Z xid)。

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

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