個人博客中曾經關於事務有過不少討論,以前的事務介紹基本都是數據庫層面的事務,本文來介紹一下J2EE中和事務相關的內容,在閱讀本文以前,但願讀者對分佈式有必定的瞭解。java
關於事務的基礎知識這裏再也不詳細介紹,想要了解的同窗能夠在個人博客中閱讀相關文章。sql
Java事務的類型有三種:JDBC事務
、JTA(Java Transaction API)事務
、容器事務
。 常見的容器事務如Spring事務,容器事務主要是J2EE應用服務器提供的,容器事務大可能是基於JTA完成,這是一個基於JNDI的,至關複雜的API實現。因此本文暫不討論容器事務。本文主要介紹J2EE開發中兩個比較基本的事務:JDBC事務
和JTA事務
。數據庫
JDBC的一切行爲包括事務是基於一個Connection
的,在JDBC中是經過Connection
對象進行事務管理。在JDBC中,經常使用的和事務相關的方法是: setAutoCommit
、commit
、rollback
等。服務器
下面看一個簡單的JDBC事務代碼:oracle
public void JdbcTransfer() { java.sql.Connection conn = null; try{ conn = conn =DriverManager.getConnection("jdbc:oracle:thin:@host:1521:SID","username","userpwd"); // 將自動提交設置爲 false, //若設置爲 true 則數據庫將會把每一次數據更新認定爲一個事務並自動提交 conn.setAutoCommit(false); stmt = conn.createStatement(); // 將 A 帳戶中的金額減小 500 stmt.execute("\ update t_account set amount = amount - 500 where account_id = 'A'"); // 將 B 帳戶中的金額增長 500 stmt.execute("\ update t_account set amount = amount + 500 where account_id = 'B'"); // 提交事務 conn.commit(); // 事務提交:轉帳的兩步操做同時成功 } catch(SQLException sqle){ try{ // 發生異常,回滾在本事務中的操作 conn.rollback(); // 事務回滾:轉帳的兩步操做徹底撤銷 stmt.close(); conn.close(); }catch(Exception ignore){ } sqle.printStackTrace(); } }
上面的代碼實現了一個簡單的轉帳功能,經過事務來控制轉帳操做,要麼都提交,要麼都回滾。框架
JDBC爲使用Java進行數據庫的事務操做提供了最基本的支持。經過JDBC事務,咱們能夠將多個SQL語句放到同一個事務中,保證其ACID特性。JDBC事務的主要優勢就是API比較簡單,能夠實現最基本的事務操做,性能也相對較好。異步
可是,JDBC事務有一個侷限:一個 JDBC 事務不能跨越多個數據庫!!!
因此,若是涉及到多數據庫的操做或者分佈式場景,JDBC事務就無能爲力了。分佈式
一般,JDBC事務就能夠解決數據的一致性等問題,鑑於他用法相對簡單,因此不少人關於Java中的事務只知道有JDBC事務,或者有人知道框架中的事務(好比Hibernate、Spring)等。可是,因爲JDBC沒法實現分佈式事務,而現在的分佈式場景愈來愈多,因此,JTA事務就應運而生。性能
若是,你在工做中沒有遇到JDBC事務沒法解決的場景,那麼只能說你作的項目還都過小。拿電商網站來講,咱們通常把一個電商網站橫向拆分紅商品模塊、訂單模塊、購物車模塊、消息模塊、支付模塊等。而後咱們把不一樣的模塊部署到不一樣的機器上,各個模塊之間經過遠程服務調用(RPC)等方式進行通訊。以一個分佈式的系統對外提供服務。網站
一個支付流程就要和多個模塊進行交互,每一個模塊都部署在不一樣的機器中,而且每一個模塊操做的數據庫都不一致,這時候就沒法使用JDBC來管理事務。咱們看一段代碼:
/** 支付訂單處理 **/ @Transactional(rollbackFor = Exception.class) public void completeOrder() { orderDao.update(); // 訂單服務本地更新訂單狀態 accountService.update(); // 調用資金帳戶服務給資金賬戶加款 pointService.update(); // 調用積分服務給積分賬戶增長積分 accountingService.insert(); // 調用會計服務向會計系統寫入會計原始憑證 merchantNotifyService.notify(); // 調用商戶通知服務向商戶發送支付結果通知 }
上面的代碼是一個簡單的支付流程的操做,其中調用了五個服務,這五個服務都經過RPC的方式調用,請問使用JDBC如何保證事務一致性?我在方法中增長了@Transactional
註解,可是因爲採用調用了分佈式服務,該事務並不能達到ACID的效果。
JTA事務比JDBC事務更強大。一個JTA事務能夠有多個參與者,而一個JDBC事務則被限定在一個單一的數據庫鏈接。下列任一個Java平臺的組件均可以參與到一個JTA事務中:JDBC
鏈接、JDO PersistenceManager
對象、JMS
隊列、JMS
主題、企業JavaBeans(EJB
)、一個用J2EE Connector Architecture
規範編譯的資源分配器。
Java事務API(Java Transaction API
,簡稱JTA ) 是一個Java企業版 的應用程序接口,在Java環境中,容許完成跨越多個XA資源的分佈式事務。
JTA和它的同胞Java事務服務(JTS;Java TransactionService),爲J2EE平臺提供了分佈式事務服務。不過JTA只是提供了一個接口,並無提供具體的實現,而是由j2ee服務器提供商 根據JTS規範提供的,常見的JTA實現有如下幾種:
JTA裏面提供了 java.transaction.UserTransaction
,裏面定義了下面幾個方法
begin
:開啓一個事務
commit
:提交當前事務
rollback
:回滾當前事務
setRollbackOnly
:把當前事務標記爲回滾
setTransactionTimeout
:設置事務的事件,超過這個事件,就拋出異常,回滾事務
這裏,值得注意的是,不是使用了UserTransaction
就能把普通的JDBC操做直接轉成JTA操做,JTA對DataSource、Connection和Resource 都是有要求的,只有符合XA規範,而且實現了XA規範的相關接口的類才能參與到JTA事務中來,關於XA規範,請看個人另一篇文章中有相關介紹。這裏,提一句,目前主流的數據庫都支持XA規範。
要想使用用 JTA 事務,那麼就須要有一個實現
javax.sql.XADataSource
、javax.sql.XAConnection
和javax.sql.XAResource
接口的 JDBC 驅動程序。一個實現了這些接口的驅動程序將能夠參與 JTA 事務。一個XADataSource
對象就是一個XAConnection
對象的工廠。XAConnection
是參與 JTA 事務的 JDBC 鏈接。要使用JTA事務,必須使用
XADataSource
來產生數據庫鏈接,產生的鏈接爲一個XA鏈接。XA鏈接(
javax.sql.XAConnection
)和非XA(java.sql.Connection
)鏈接的區別在於:XA能夠參與JTA的事務,並且不支持自動提交。
public void JtaTransfer() { javax.transaction.UserTransaction tx = null; java.sql.Connection conn = null; try{ tx = (javax.transaction.UserTransaction) context.lookup("java:comp/UserTransaction"); //取得JTA事務,本例中是由Jboss容器管理 javax.sql.DataSource ds = (javax.sql.DataSource) context.lookup("java:/XAOracleDS"); //取得數據庫鏈接池,必須有支持XA的數據庫、驅動程序 tx.begin(); conn = ds.getConnection(); // 將自動提交設置爲 false, //若設置爲 true 則數據庫將會把每一次數據更新認定爲一個事務並自動提交 conn.setAutoCommit(false); stmt = conn.createStatement(); // 將 A 帳戶中的金額減小 500 stmt.execute("\ update t_account set amount = amount - 500 where account_id = 'A'"); // 將 B 帳戶中的金額增長 500 stmt.execute("\ update t_account set amount = amount + 500 where account_id = 'B'"); // 提交事務 tx.commit(); // 事務提交:轉帳的兩步操做同時成功 } catch(SQLException sqle){ try{ // 發生異常,回滾在本事務中的操作 tx.rollback(); // 事務回滾:轉帳的兩步操做徹底撤銷 stmt.close(); conn.close(); }catch(Exception ignore){ } sqle.printStackTrace(); } }
上面的例子就是一個使用JTA事務的轉帳操做,該操做相對依賴於J2EE容器,而且須要經過JNDI的方式獲取UserTransaction
和Connection
。
一個分佈式事務(Distributed Transaction)包括一個事務管理器(
transaction manager
)和一個或多個資源管理器(resource manager
)。一個資源管理器(resource manager
)是任意類型的持久化數據存儲。事務管理器(transaction manager
)承擔着全部事務參與單元者的相互通信的責任。
JTA的實現方式也是基於以上這些分佈式事務參與者實現的,具體的關於JTA的實現細節不是本文的重點,感興趣的同窗能夠閱讀JTA 深度歷險 – 原理與實現
JTA的優勢很明顯,就是提供了分佈式事務的解決方案,嚴格的ACID。可是,標準的JTA方式的事務管理在平常開發中並不經常使用,由於他有不少缺點:
Java事務的類型有三種:JDBC事務
、JTA(Java Transaction API)事務
、容器事務
,其中JDBC的事務操做用法比較簡單,適合於處理同一個數據源的操做。JTA事務相對複雜,能夠用於處理跨多個數據庫的事務,是分佈式事務的一種解決方案。
這裏還要簡單說一下,雖然JTA事務是Java提供的可用於分佈式事務的一套API,可是不一樣的J2EE平臺的實現都不同,而且都不是很方便使用,因此,通常在項目中不太使用這種較爲負責的API。如今業內比較經常使用的分佈式事務解決方案主要有異步消息確保型、TCC、最大努力通知等。關於這幾種分佈式事務解決方案,我會在後面的文章中介紹。歡迎關注與交流。