JAVA 事務處理

 Java事務的類型有三種。

JDBC事務、JTA(Java Transaction API)事務、容器事務java

一、JDBC事務

  JDBC 事務是用 Connection 對象控制的。JDBC Connection 接口( java.sql.Connection )提供了兩種事務模式:自動提交和手工提交。 java.sql.Connection 提供瞭如下控制事務的方法:程序員

public void setAutoCommit(boolean)  
public boolean getAutoCommit()  
public void commit()  
public void rollback()

  使用 JDBC 事務界定時,您能夠將多個 SQL 語句結合到一個事務中。JDBC 事務的一個缺點是事務的範圍侷限於一個數據庫鏈接。一個 JDBC 事務不能跨越多個數據庫。sql

二、JTA 

       JTA是一種高層的,與實現無關的,與協議無關的API,應用程序和應用服務器可使用JTA來訪問事務。數據庫

  JTA容許應用程序執行分佈式事務處理——在兩個或多個網絡計算機資源上訪問而且更新數據,這些數據能夠分佈在多個數據庫上。JDBC驅動程序的JTA支持極大地加強了數據訪問能力。服務器

  若是計劃用 JTA 界定事務,那麼就須要有一個實現 javax.sql.XADataSource 、 javax.sql.XAConnection 和 javax.sql.XAResource 接口的 JDBC 驅動程序。一個實現了這些接口的驅動程序將能夠參與 JTA 事務。一個 XADataSource 對象就是一個 XAConnection 對象的工廠。 XAConnections 是參與 JTA 事務的 JDBC 鏈接。網絡

  您將須要用應用服務器的管理工具設置 XADataSource。(從應用服務器和 JDBC 驅動程序的文檔中能夠了解到相關的指導)架構

  J2EE應用程序用 JNDI 查詢數據源。一旦應用程序找到了數據源對象,它就調用 javax.sql.DataSource.getConnection() 以得到到數據庫的鏈接。分佈式

  XA 鏈接與非 XA 鏈接不一樣。必定要記住 XA 鏈接參與了 JTA 事務。這意味着 XA 鏈接不支持 JDBC 的自動提交功能。同時,應用程序必定不要對 XA 鏈接調用 java.sql.Connection.commit() 或者 java.sql.Connection.rollback() .相反,應用程序應該使用 UserTransaction.begin()、 UserTransaction.commit() 和 UserTransaction.rollback()工具

2.1 JTA 分佈式事務處理(注意:connA 和 connB 是來自不一樣數據庫的鏈接)
public void transferAccount() { 
 UserTransaction userTx = null; 
 Connection connA = null; 
 Statement stmtA = null; 
 Connection connB = null; 
 Statement stmtB = null; 
    
 try{ 
       // 得到 Transaction 管理對象
 userTx = (UserTransaction)getContext().lookup("\
       java:comp/UserTransaction"); 
 // 從數據庫 A 中取得數據庫鏈接
 connA = getDataSourceA().getConnection(); 
 // 從數據庫 B 中取得數據庫鏈接
 connB = getDataSourceB().getConnection(); 
      
                        // 啓動事務
 userTx.begin();
 // 將 A 帳戶中的金額減小 500 
 stmtA = connA.createStatement(); 
 stmtA.execute("
            update t_account set amount = amount - 500 where account_id = 'A'");
 // 將 B 帳戶中的金額增長 500 
 stmtB = connB.createStatement(); 
 stmtB.execute("\
             update t_account set amount = amount + 500 where account_id = 'B'");
 // 提交事務
 userTx.commit();
 // 事務提交:轉帳的兩步操做同時成功(數據庫 A 和數據庫 B 中的數據被同時更新)
 } catch(SQLException sqle){ 
 try{ 
         // 發生異常,回滾在本事務中的操縱
                  userTx.rollback();
 // 事務回滾:轉帳的兩步操做徹底撤銷 
 //( 數據庫 A 和數據庫 B 中的數據更新被同時撤銷)
 stmt.close(); 
                 conn.close(); 
 ... 
 }catch(Exception ignore){ 
 } 
 sqle.printStackTrace(); 
 } catch(Exception ne){ 
 e.printStackTrace(); 
 } 
 }

三、容器事務

  容器事務主要是J2EE應用服務器提供的,容器事務大可能是基於JTA完成,這是一個基於JNDI的,至關複雜的API實現。相對編碼實現JTA事務管理,咱們能夠經過EJB容器提供的容器事務管理機制(CMT)完成同一個功能,這項功能由J2EE應用服務器提供。這使得咱們能夠簡單的指定將哪一個方法加入事務,一旦指定,容器將負責事務管理任務。這是咱們土建的解決方式,由於經過這種方式咱們能夠將事務代碼排除在邏輯編碼以外,同時將全部困難交給J2EE容器去解決。使用EJB CMT的另一個好處就是程序員無需關心JTA API的編碼,不過,理論上咱們必須使用EJB.編碼

四、上述三種事務的差別

    一、JDBC事務控制的侷限性在一個數據庫鏈接內,可是其使用簡單。

    二、JTA事務的功能強大,事務能夠跨越多個數據庫或多個DAO,使用也比較複雜。

    三、容器事務,主要指的是J2EE應用服務器提供的事務管理,侷限於EJB應用使用。

五、JTA 實現原理

JTA 實現原理-參考博文http://www.ibm.com/developerworks/cn/java/j-lo-jta/

不少開發人員都會對 JTA 的內部工做機制感興趣:我編寫的代碼沒有任何與事務資源(如數據庫鏈接)互動的代碼,可是個人操做(數據庫更新)卻實實在在的被包含在了事務中,那 JTA 到底是經過何種方式來實現這種透明性的呢? 要理解 JTA 的實現原理首先須要瞭解其架構:它包括事務管理器(Transaction Manager)和一個或多個支持 XA 協議的資源管理器 ( Resource Manager ) 兩部分, 咱們能夠將資源管理器看作任意類型的持久化數據存儲;事務管理器則承擔着全部事務參與單元的協調與控制。 根據所面向對象的不一樣,咱們能夠將 JTA 的事務管理器和資源管理器理解爲兩個方面:面向開發人員的使用接口(事務管理器)和麪向服務提供商的實現接口(資源管理器)。其中開發接口的主要部分即爲上述示例中引用的 UserTransaction 對象,開發人員經過此接口在信息系統中實現分佈式事務;而實現接口則用來規範提供商(如數據庫鏈接提供商)所提供的事務服務,它約定了事務的資源管理功能,使得 JTA 能夠在異構事務資源之間執行協同溝通。以數據庫爲例,IBM 公司提供了實現分佈式事務的數據庫驅動程序,Oracle 也提供了實現分佈式事務的數據庫驅動程序, 在同時使用 DB2 和 Oracle 兩種數據庫鏈接時, JTA 便可以根據約定的接口協調者兩種事務資源從而實現分佈式事務。正是基於統一規範的不一樣實現使得 JTA 能夠協調與控制不一樣數據庫或者 JMS 廠商的事務資源,其架構以下圖所示:

下面將經過具體的代碼向讀者介紹 JTA 實現原理。下圖列出了示例實現中涉及到的 Java 類,其中 UserTransactionImpl 實現了 UserTransaction 接口,TransactionManagerImpl 實現了 TransactionManager 接口,TransactionImpl 實現了 Transaction 接口。

清單 3. 開始事務 - 
UserTransactionImpl implenments UserTransaction
public void begin() throws NotSupportedException, SystemException { 
   // 將開始事務的操做委託給 TransactionManagerImpl 
   TransactionManagerImpl.singleton().begin(); 
     }
清單 4. 開始事務 - 
TransactionManagerImpl implements TransactionManager
// 此處 transactionHolder 用於將 Transaction 所表明的事務對象關聯到線程上
private static ThreadLocal<TransactionImpl> transactionHolder 
        = new ThreadLocal<TransactionImpl>(); 
 //TransacationMananger 必須維護一個全局對象,所以使用單實例模式實現
 private static TransactionManagerImpl singleton = new TransactionManagerImpl(); 
 private TransactionManagerImpl(){ 
 } 
 public static TransactionManagerImpl singleton(){ 
 return singleton; 
 } 
 public void begin() throws NotSupportedException, SystemException { 
 //XidImpl 實現了 Xid 接口,其做用是惟一標識一個事務
 XidImpl xid = new XidImpl(); 
 // 建立事務對象,並將對象關聯到線程
 TransactionImpl tx = new TransactionImpl(xid); 
 transactionHolder.set(tx); 
 }

如今咱們就能夠理解 Transaction 接口上沒有定義 begin 方法的緣由了:Transaction 對象自己就表明了一個事務,在它被建立的時候就代表事務已經開始,所以也就不須要額外定義 begin() 方法了。

清單 5. 提交事務 -
UserTransactionImpl implenments UserTransaction
      public void commit() throws RollbackException, HeuristicMixedException, 
 HeuristicRollbackException, SecurityException, 
 IllegalStateException, SystemException { 
 // 檢查是不是 Roll back only 事務,若是是回滾事務
        if(rollBackOnly){ 
     rollback(); 
     return; 
       } else { 
    // 將提交事務的操做委託給 TransactionManagerImpl 
    TransactionManagerImpl.singleton().commit(); 
       } 
 }
清單 6. 提交事務 - 
TransactionManagerImpl implenments TransactionManager
public void commit() throws RollbackException, HeuristicMixedException, 
    HeuristicRollbackException, SecurityException, 
    IllegalStateException, SystemException { 
     // 取得當前事務所關聯的事務並經過其 commit 方法提交
     TransactionImpl tx = transactionHolder.get(); 
     tx.commit(); 
         }

同理, rollback、getStatus、setRollbackOnly 等方法也採用了與 commit() 相同的方式實現。 UserTransaction 對象不會對事務進行任何控制, 全部的事務方法都是經過 TransactionManager 傳遞到實際的事務資源即 Transaction 對象上。

上述示例演示了 JTA 事務的處理過程,下面將爲您展現事務資源(數據庫鏈接,JMS)是如何以透明的方式加入到 JTA 事務中的。首先須要明確的一點是,在 JTA 事務 代碼中得到的數據庫源 ( DataSource ) 必須是支持分佈式事務的。在以下的代碼示例中,儘管全部的數據庫操做都被包含在了 JTA 事務中,可是由於 MySql 的數據庫鏈接是經過本地方式得到的,對 MySql 的任何更新將不會被自動包含在全局事務中。

清單 7. JTA 事務處理
 public void transferAccount() { 
 UserTransaction userTx = null; 
 Connection mySqlConnection = null; 
 Statement mySqlStat = null; 
 Connection connB = null; 
 Statement stmtB = null; 
    
 try{ 
        // 得到 Transaction 管理對象
 userTx = (UserTransaction)getContext().lookup("java:comp/UserTransaction");
 // 以本地方式得到 mySql 數據庫鏈接
 mySqlConnection = DriverManager.getConnection("localhost:1111"); 
 // 從數據庫 B 中取得數據庫鏈接, getDataSourceB 返回應用服務器的數據源
 connB = getDataSourceB().getConnection(); 
      
 // 啓動事務
 userTx.begin();
 // 將 A 帳戶中的金額減小 500 
 //mySqlConnection 是從本地得到的數據庫鏈接,不會被包含在全局事務中
 mySqlStat = mySqlConnection.createStatement(); 
 mySqlStat.execute("update t_account set amount = amount - 500 where account_id = 'A'");
 //connB 是從應用服務器得的數據庫鏈接,會被包含在全局事務中
 stmtB = connB.createStatement(); 
 stmtB.execute("update t_account set amount = amount + 500 where account_id = 'B'");
 // 事務提交:connB 的操做被提交,mySqlConnection 的操做不會被提交
 userTx.commit();
 } catch(SQLException sqle){ 
 // 處理異常代碼
 } catch(Exception ne){ 
 e.printStackTrace(); 
 } 
 }

爲何必須從支持事務的數據源中得到的數據庫鏈接才支持分佈式事務呢?其實支持事務的數據源與普通的數據源是不一樣的,它實現了額外的 XADataSource 接口。咱們能夠簡單的將 XADataSource 理解爲普通的數據源(繼承了 java.sql.PooledConnection),只是它爲支持分佈式事務而增長了 getXAResource 方法。另外,由 XADataSource 返回的數據庫鏈接與普通鏈接也是不一樣的,此鏈接除了實現 java.sql.Connection 定義的全部功能以外還實現了 XAConnection 接口。咱們能夠把 XAConnection 理解爲普通的數據庫鏈接,它支持全部 JDBC 規範的數據庫操做,不一樣之處在於 XAConnection 增長了對分佈式事務的支持。經過下面的類圖讀者能夠對這幾個接口的關係有所瞭解:

相關文章
相關標籤/搜索