JTA概要介紹
Java事務(wù)API(JTA;Java Transaction API)和它的同胞Java事務(wù)服務(wù)(JTS;Java Transaction Service),為J2EE平臺提供了分布式事務(wù)服務(wù)。一個分布式事務(wù)(distributed transaction)包括一個事務(wù)管理器(transaction manager)和一個或多個資源管理器(resource manager)。一個資源管理器(resource manager)是任意類型的持久化數(shù)據(jù)存儲。事務(wù)管理器(transaction manager)承擔(dān)著所有事務(wù)參與單元者的相互通訊的責(zé)任。下車站顯示了事務(wù)管理器和資源管理的間的關(guān)系。
JTA事務(wù)比JDBC事務(wù)更強(qiáng)大。一個JTA事務(wù)可以有多個參與者,而一個JDBC事務(wù)則被限定在一個單一的數(shù)據(jù)庫連接。下列任一個Java平臺的組件都可以參與到一個JTA事務(wù)中:
.JDBC連接
.JDO PersistenceManager 對象
.JMS 隊(duì)列
.JMS 主題
.企業(yè)JavaBeans(EJB)
.一個用J2EE Connector Architecture 規(guī)范編譯的資源分配器。
使用JTA的事務(wù)劃分
要用JTA來劃分一個事務(wù),應(yīng)用程序調(diào)用javax.transaction.UserTransaction接口中的方法。示例4顯示了一個典型的JNDI搜索的UseTransaction對象。
應(yīng)用程序有了UserTransaction對象的引用之后,就可以象示例5那樣來起動事務(wù)。
當(dāng)應(yīng)用程序調(diào)用commit()時,事務(wù)管理器使用兩段提交協(xié)議來結(jié)束事務(wù)。JTA事務(wù)控制的方法:
.javax.transaction.UserTransaction接口提供了下列事務(wù)控制方法:
應(yīng)用程序調(diào)用begin()來起動事務(wù),即可調(diào)用commit()也可以調(diào)用rollback()來結(jié)束事務(wù)。
使用JTA和JDBC
開發(fā)人員經(jīng)常使用JDBC來作為DAO類中的底層數(shù)據(jù)操作。如果計劃使用JTA來劃分事務(wù),你將需要一個實(shí)現(xiàn)了javax.sql.XADataSource,javax.sql.XAConnection和javax.sql.XAResource接口JDBC的驅(qū)動。實(shí)現(xiàn)了這些接口的驅(qū)動將有能力參與到JTA事務(wù)中。一個XADataSource對象是一個XAConnection對象的工廠。XAConnections是參與到JTA事務(wù)中的連接。
你需要使用應(yīng)用程序服務(wù)器管理工具來建立XADataSource對象。對于特殊的指令請參考應(yīng)用程序服務(wù)器文檔和JDBC驅(qū)動文檔。
J2EE應(yīng)用程序使用JNDI來查找數(shù)據(jù)源。一旦應(yīng)用程序有了一個數(shù)據(jù)源對象的引用,這會調(diào)用javax.sql.DataSource.getConnection()來獲得數(shù)據(jù)庫的連接。
XA連接區(qū)別于非XA連接。要記住的是XA連接是一個JTA事務(wù)中的參與者。這就意味著XA連接不支持JDBC的自動提交特性。也就是說應(yīng)用程序不必在XA連接上調(diào)用java.sql.Connection.commit()或java.sql.Connection.rollback()。相反,應(yīng)用程序應(yīng)該使用UserTransaction.begin()、UserTransaction.commit()和UserTransaction.rollback().
選擇最好的方法
我們已經(jīng)討論了JDBC和JTA是怎樣劃分事務(wù)的。每一種方法都有它的優(yōu)點(diǎn),回此你需要決定為你的應(yīng)用程序選擇一個最適應(yīng)的方法。 在我們團(tuán)隊(duì)許多最近的對于事務(wù)劃分的項(xiàng)目中使用JDBC API來創(chuàng)建DAO類。這DAO類總結(jié)如下:
.事務(wù)劃分代碼被嵌入到DAO類內(nèi)部
.DAO類使用JDBC API來進(jìn)行事務(wù)劃分
.調(diào)用者沒有劃分事務(wù)的方法
.事務(wù)范圍被限定在一個單一的JDBC連接
JDBC事務(wù)對復(fù)雜的企業(yè)應(yīng)用程序不總是有效的。如果你的事務(wù)將跨越多個DAO對象或多個數(shù)據(jù)庫,那么下面的實(shí)現(xiàn)策略可能會更恰當(dāng):
.用JTA對事務(wù)進(jìn)行劃分
.事務(wù)劃分代碼被DAO分開
.調(diào)用者承擔(dān)劃分事務(wù)的責(zé)任
.DAO參與一個全局的事務(wù)中
JDBC方法由于它的簡易性而具有吸引力,JTA方法提供了更多靈活性。你選擇什么樣的實(shí)現(xiàn)將依賴于你的應(yīng)用程序的特定需求。
日志記錄和DAO
一個好的DAO實(shí)現(xiàn)類將使用日志記錄來捕獲有關(guān)它在運(yùn)行時的行為細(xì)節(jié)。你可以選擇記錄異常、配置信息、連接狀態(tài)、JDBC驅(qū)動程序的元數(shù)據(jù)或查詢參數(shù)。日志對開發(fā)整個階段都是有益的。我經(jīng)常檢查應(yīng)用程序在開發(fā)期間、測試期間和產(chǎn)品中的日志記錄。
在這段中,我們將展現(xiàn)一段如何把Jakarta Commaons Logging結(jié)合中一個DAO中的例子。在我們開始之前,讓我們先回顧一些基礎(chǔ)知識。
選擇一個日志例庫
許多開發(fā)人員使用的基本日志形式是:System.out.println和System.err.println.Println語句。這種形式快捷方便,但它們不能提供一個完整的日志系統(tǒng)的的能力。下表列出了Java平臺的日志類庫:
Java.util.logging是J2SE1.4平臺上的標(biāo)準(zhǔn)的API。但是,大多數(shù)開發(fā)人員都認(rèn)為Jakarta Log4j提供了更大的功能性和靈活性。Log4j超越j(luò)ava.util.logging的優(yōu)點(diǎn)之一就是它支持J2SE1.3和J2SE1.4平臺。
Jakarta Commons Logging能夠被用于和java.util.loggin或Jakarta Log4j一起工作。Commons Logging是一個把你的應(yīng)用程序獨(dú)立于日志實(shí)現(xiàn)的提取層。使用Commons Logging你能夠通過改變一個配置文件來與下面的日志實(shí)現(xiàn)來交換數(shù)據(jù)。Commons Logging被用于JAKARTA Struts1.1和Jakarta HttpClient2.0中。
一個日志示例
示例7顯示了在一個DOA類中怎樣使用Jakarta Commons Logging
日志是評估應(yīng)用程序的基本部分。如果你在一個DAO中遇到了失敗,日志經(jīng)常會為理解發(fā)生的什么錯誤提供最好的信息。把日志結(jié)合到你的DAO中,確保得到調(diào)試和解決問題的有效手段。
DAO中的異常處理
我們已經(jīng)看了事務(wù)劃分和日志記錄,并且現(xiàn)在對于它們是怎樣應(yīng)用于數(shù)據(jù)訪問對象的有一個深入的理解。我們第三部分也是最后要討論的是異常處理。下面的一些簡單的異常處理方針使用你的DAO更容易使用,更加健壯和更具有可維護(hù)性。
在實(shí)現(xiàn)DAO模式的時候,要考濾下面的問題:
.在DAO的public接口中的方法將拋出被檢查的異常嗎?
.如果是,將拋出什么樣的檢查性異常?
.在DAO實(shí)現(xiàn)類中怎能樣處理異常。
在用DAO模式工作的過程中,我們的團(tuán)隊(duì)為異常處理開發(fā)了一組方針。下面的這些方針會很大程度的改善你的DAO:
.DAO方法應(yīng)該拋出有意義的異常。
.DAO方法不應(yīng)該拋出java.lang.Exception異常。因?yàn)閖ava.lang.Exception太一般化,它不能包含有關(guān)潛在問題的所有信息。
.DAO方法不應(yīng)該拋出java.sql.SQLException異常。SQLException是一個底層的JDBC異常,DAO應(yīng)用努力封裝JDBC異常而不應(yīng)該把JDBC異常留給應(yīng)用程序的其它部分。
.在DAO接口中的方法應(yīng)該只拋出調(diào)用者期望處理的檢查性異常。如果調(diào)用者不能用適當(dāng)?shù)姆椒▉硖幚懋惓#紴V拋出不檢查性(運(yùn)行時run-time)異常。
.如果你的數(shù)據(jù)訪問代碼捕獲了一個異常,不可要忽略它。忽略捕獲異常的DAO是很處理的。
.使用異常鏈把底層的異常傳遞給高層的某個處理器。
.考濾定義一個標(biāo)準(zhǔn)的DAO異常類。Spring框架提供了一個優(yōu)秀的預(yù)定義的DAO異常類的集合。
看Resources,查看有異常和異常處理技術(shù)的更詳細(xì)信息。
實(shí)現(xiàn)示例:MovieDAO
MoveDAO是一個示范了在這篇文章中所討論的所有技術(shù),包括事務(wù)劃分、日志記錄和異常處理。你會在Resources段找到MovieDAO的源代碼。它被分下面的三個包:
這個DAO模式的實(shí)現(xiàn)由下面的類和接口組成:
MovieDAO接口定義了DAO的數(shù)據(jù)操作。這個接口有如下五個方法:
daoexamples.movie包包含了兩個MovieDAO接口的實(shí)現(xiàn)。每個實(shí)現(xiàn)使用了一個同的事務(wù)劃分方法,如下表所示:
MovieDAO 示范應(yīng)用程序
這個示范應(yīng)用程序是一個叫做daoexamples.moviedemo.DemoServlet.DemoServlet的servlet類,它使用Movie DAO來查詢和更新一個表中的movie數(shù)據(jù)。
這個servlet示范了把JTA感知的MovieDAO和Java消息服務(wù)組合到一個單一的事務(wù)中,如示例8所示:
要運(yùn)行這個范例應(yīng)用程序,在你的應(yīng)用程序服務(wù)器中配置一個XA 數(shù)據(jù)源和一個非XA數(shù)據(jù)源。然后布署daoexamples.ear文件。這個應(yīng)用程序?qū)⑦\(yùn)行在任何與J2EE兼容的應(yīng)用程序服務(wù)器。
Java事務(wù)API(JTA;Java Transaction API)和它的同胞Java事務(wù)服務(wù)(JTS;Java Transaction Service),為J2EE平臺提供了分布式事務(wù)服務(wù)。一個分布式事務(wù)(distributed transaction)包括一個事務(wù)管理器(transaction manager)和一個或多個資源管理器(resource manager)。一個資源管理器(resource manager)是任意類型的持久化數(shù)據(jù)存儲。事務(wù)管理器(transaction manager)承擔(dān)著所有事務(wù)參與單元者的相互通訊的責(zé)任。下車站顯示了事務(wù)管理器和資源管理的間的關(guān)系。
JTA事務(wù)比JDBC事務(wù)更強(qiáng)大。一個JTA事務(wù)可以有多個參與者,而一個JDBC事務(wù)則被限定在一個單一的數(shù)據(jù)庫連接。下列任一個Java平臺的組件都可以參與到一個JTA事務(wù)中:
.JDBC連接
.JDO PersistenceManager 對象
.JMS 隊(duì)列
.JMS 主題
.企業(yè)JavaBeans(EJB)
.一個用J2EE Connector Architecture 規(guī)范編譯的資源分配器。
使用JTA的事務(wù)劃分
要用JTA來劃分一個事務(wù),應(yīng)用程序調(diào)用javax.transaction.UserTransaction接口中的方法。示例4顯示了一個典型的JNDI搜索的UseTransaction對象。
import javax.transaction.*; import javax.naming.*; // ... InitialContext ctx = new InitialContext(); Object txObj = ctx.lookup(";java:comp/UserTransaction";); UserTransaction utx = (UserTransaction) txObj; |
應(yīng)用程序有了UserTransaction對象的引用之后,就可以象示例5那樣來起動事務(wù)。
utx.begin(); // ... DataSource ds = obtainXADataSource(); Connection conn = ds.getConnection(); pstmt = conn.prepareStatement(";UPDATE MOVIES ...";); pstmt.setString(1, ";Spinal Tap";); pstmt.executeUpdate(); // ... utx.commit(); // ... |
當(dāng)應(yīng)用程序調(diào)用commit()時,事務(wù)管理器使用兩段提交協(xié)議來結(jié)束事務(wù)。JTA事務(wù)控制的方法:
.javax.transaction.UserTransaction接口提供了下列事務(wù)控制方法:
.public void begin() .public void commit() .public void rollback() .public void getStatus() .public void setRollbackOnly() .public void setTransactionTimeout(int) |
應(yīng)用程序調(diào)用begin()來起動事務(wù),即可調(diào)用commit()也可以調(diào)用rollback()來結(jié)束事務(wù)。
使用JTA和JDBC
開發(fā)人員經(jīng)常使用JDBC來作為DAO類中的底層數(shù)據(jù)操作。如果計劃使用JTA來劃分事務(wù),你將需要一個實(shí)現(xiàn)了javax.sql.XADataSource,javax.sql.XAConnection和javax.sql.XAResource接口JDBC的驅(qū)動。實(shí)現(xiàn)了這些接口的驅(qū)動將有能力參與到JTA事務(wù)中。一個XADataSource對象是一個XAConnection對象的工廠。XAConnections是參與到JTA事務(wù)中的連接。
你需要使用應(yīng)用程序服務(wù)器管理工具來建立XADataSource對象。對于特殊的指令請參考應(yīng)用程序服務(wù)器文檔和JDBC驅(qū)動文檔。
J2EE應(yīng)用程序使用JNDI來查找數(shù)據(jù)源。一旦應(yīng)用程序有了一個數(shù)據(jù)源對象的引用,這會調(diào)用javax.sql.DataSource.getConnection()來獲得數(shù)據(jù)庫的連接。
XA連接區(qū)別于非XA連接。要記住的是XA連接是一個JTA事務(wù)中的參與者。這就意味著XA連接不支持JDBC的自動提交特性。也就是說應(yīng)用程序不必在XA連接上調(diào)用java.sql.Connection.commit()或java.sql.Connection.rollback()。相反,應(yīng)用程序應(yīng)該使用UserTransaction.begin()、UserTransaction.commit()和UserTransaction.rollback().
選擇最好的方法
我們已經(jīng)討論了JDBC和JTA是怎樣劃分事務(wù)的。每一種方法都有它的優(yōu)點(diǎn),回此你需要決定為你的應(yīng)用程序選擇一個最適應(yīng)的方法。 在我們團(tuán)隊(duì)許多最近的對于事務(wù)劃分的項(xiàng)目中使用JDBC API來創(chuàng)建DAO類。這DAO類總結(jié)如下:
.事務(wù)劃分代碼被嵌入到DAO類內(nèi)部
.DAO類使用JDBC API來進(jìn)行事務(wù)劃分
.調(diào)用者沒有劃分事務(wù)的方法
.事務(wù)范圍被限定在一個單一的JDBC連接
JDBC事務(wù)對復(fù)雜的企業(yè)應(yīng)用程序不總是有效的。如果你的事務(wù)將跨越多個DAO對象或多個數(shù)據(jù)庫,那么下面的實(shí)現(xiàn)策略可能會更恰當(dāng):
.用JTA對事務(wù)進(jìn)行劃分
.事務(wù)劃分代碼被DAO分開
.調(diào)用者承擔(dān)劃分事務(wù)的責(zé)任
.DAO參與一個全局的事務(wù)中
JDBC方法由于它的簡易性而具有吸引力,JTA方法提供了更多靈活性。你選擇什么樣的實(shí)現(xiàn)將依賴于你的應(yīng)用程序的特定需求。
日志記錄和DAO
一個好的DAO實(shí)現(xiàn)類將使用日志記錄來捕獲有關(guān)它在運(yùn)行時的行為細(xì)節(jié)。你可以選擇記錄異常、配置信息、連接狀態(tài)、JDBC驅(qū)動程序的元數(shù)據(jù)或查詢參數(shù)。日志對開發(fā)整個階段都是有益的。我經(jīng)常檢查應(yīng)用程序在開發(fā)期間、測試期間和產(chǎn)品中的日志記錄。
在這段中,我們將展現(xiàn)一段如何把Jakarta Commaons Logging結(jié)合中一個DAO中的例子。在我們開始之前,讓我們先回顧一些基礎(chǔ)知識。
選擇一個日志例庫
許多開發(fā)人員使用的基本日志形式是:System.out.println和System.err.println.Println語句。這種形式快捷方便,但它們不能提供一個完整的日志系統(tǒng)的的能力。下表列出了Java平臺的日志類庫:
日志類庫 | 開源嗎? | URL |
Java.util.logging | 否 | http://java.sun.com/j2ee |
Jakarta Log4j | 是 | http://hajarta.apache.org/log4j/ |
Jakarta Commons Logging | 是 | http:/Jakarta.apache.org/commons/logging.html |
Java.util.logging是J2SE1.4平臺上的標(biāo)準(zhǔn)的API。但是,大多數(shù)開發(fā)人員都認(rèn)為Jakarta Log4j提供了更大的功能性和靈活性。Log4j超越j(luò)ava.util.logging的優(yōu)點(diǎn)之一就是它支持J2SE1.3和J2SE1.4平臺。
Jakarta Commons Logging能夠被用于和java.util.loggin或Jakarta Log4j一起工作。Commons Logging是一個把你的應(yīng)用程序獨(dú)立于日志實(shí)現(xiàn)的提取層。使用Commons Logging你能夠通過改變一個配置文件來與下面的日志實(shí)現(xiàn)來交換數(shù)據(jù)。Commons Logging被用于JAKARTA Struts1.1和Jakarta HttpClient2.0中。
一個日志示例
示例7顯示了在一個DOA類中怎樣使用Jakarta Commons Logging
import org.apache.commons.logging.*; class DocumentDAOImpl implements DocumentDAO { static private final Log log = LogFactory.getLog(DocumentDAOImpl.class); public void deleteDocument(String id) { // ... log.debug(";deleting document: "; + id); // ... try { // ... data operations ... } catch (SomeException ex) { log.error(";Unable to delete document"; ex); // ... handle the exception ... } } } |
日志是評估應(yīng)用程序的基本部分。如果你在一個DAO中遇到了失敗,日志經(jīng)常會為理解發(fā)生的什么錯誤提供最好的信息。把日志結(jié)合到你的DAO中,確保得到調(diào)試和解決問題的有效手段。
DAO中的異常處理
我們已經(jīng)看了事務(wù)劃分和日志記錄,并且現(xiàn)在對于它們是怎樣應(yīng)用于數(shù)據(jù)訪問對象的有一個深入的理解。我們第三部分也是最后要討論的是異常處理。下面的一些簡單的異常處理方針使用你的DAO更容易使用,更加健壯和更具有可維護(hù)性。
在實(shí)現(xiàn)DAO模式的時候,要考濾下面的問題:
.在DAO的public接口中的方法將拋出被檢查的異常嗎?
.如果是,將拋出什么樣的檢查性異常?
.在DAO實(shí)現(xiàn)類中怎能樣處理異常。
在用DAO模式工作的過程中,我們的團(tuán)隊(duì)為異常處理開發(fā)了一組方針。下面的這些方針會很大程度的改善你的DAO:
.DAO方法應(yīng)該拋出有意義的異常。
.DAO方法不應(yīng)該拋出java.lang.Exception異常。因?yàn)閖ava.lang.Exception太一般化,它不能包含有關(guān)潛在問題的所有信息。
.DAO方法不應(yīng)該拋出java.sql.SQLException異常。SQLException是一個底層的JDBC異常,DAO應(yīng)用努力封裝JDBC異常而不應(yīng)該把JDBC異常留給應(yīng)用程序的其它部分。
.在DAO接口中的方法應(yīng)該只拋出調(diào)用者期望處理的檢查性異常。如果調(diào)用者不能用適當(dāng)?shù)姆椒▉硖幚懋惓#紴V拋出不檢查性(運(yùn)行時run-time)異常。
.如果你的數(shù)據(jù)訪問代碼捕獲了一個異常,不可要忽略它。忽略捕獲異常的DAO是很處理的。
.使用異常鏈把底層的異常傳遞給高層的某個處理器。
.考濾定義一個標(biāo)準(zhǔn)的DAO異常類。Spring框架提供了一個優(yōu)秀的預(yù)定義的DAO異常類的集合。
看Resources,查看有異常和異常處理技術(shù)的更詳細(xì)信息。
實(shí)現(xiàn)示例:MovieDAO
MoveDAO是一個示范了在這篇文章中所討論的所有技術(shù),包括事務(wù)劃分、日志記錄和異常處理。你會在Resources段找到MovieDAO的源代碼。它被分下面的三個包:
.daoexamples.exception .daoexamples.move .daoexamples.moviedemo |
這個DAO模式的實(shí)現(xiàn)由下面的類和接口組成:
.daoexamples.movie.MovieDAOFactory .daoexamples.movie.MovieDAO .daoexamples.movie.MovieDAOImpl .daoexamples.movie.MovieDAOImplJTA .daoexamples.movie.Movie .daoexamples.movie.MovieImple .daoexamples.movie.MovieNotFoundException .daoexamples.movie.MovieUtil |
MovieDAO接口定義了DAO的數(shù)據(jù)操作。這個接口有如下五個方法:
.public Movie findMovieById(String id) .public java.util.Collection findMoviesByYear(String year) .public void deleteMovie(String id) .public Movie createMovie(String rating,String year,String title) .public void updateMovie(String id,String rating,String year,String title) |
daoexamples.movie包包含了兩個MovieDAO接口的實(shí)現(xiàn)。每個實(shí)現(xiàn)使用了一個同的事務(wù)劃分方法,如下表所示:
MovieDAOImpl | MovieDAOImplJTA | |
實(shí)現(xiàn)了MovieDAO接口嗎? | Yes | Yes |
通過JNDI獲得DataSource嗎? | Yes | Yes |
從一個DataSource獲得java.sql.Connection對象嗎? | Yes | Yes |
DAO界定內(nèi)部的事務(wù)嗎? | Yes | No |
使用JDBC事務(wù)嗎? | Yes | No |
使用一個XA DataSource嗎? | No | Yes |
分擔(dān)JTA事務(wù)嗎? | No | Yes |
MovieDAO 示范應(yīng)用程序
這個示范應(yīng)用程序是一個叫做daoexamples.moviedemo.DemoServlet.DemoServlet的servlet類,它使用Movie DAO來查詢和更新一個表中的movie數(shù)據(jù)。
這個servlet示范了把JTA感知的MovieDAO和Java消息服務(wù)組合到一個單一的事務(wù)中,如示例8所示:
UserTransaction utx = MovieUtil.getUserTransaction(); utx.begin(); batman = dao.createMovie(";R"; ";2008"; ";Batman Reloaded";); publisher = new MessagePublisher(); publisher.publishTextMessage(";I’ll be back";); dao.updateMovie(topgun.getId(), ";PG-13"; topgun.getReleaseYear(), topgun.getTitle()); dao.deleteMovie(legallyblonde.getId()); utx.commit(); |
要運(yùn)行這個范例應(yīng)用程序,在你的應(yīng)用程序服務(wù)器中配置一個XA 數(shù)據(jù)源和一個非XA數(shù)據(jù)源。然后布署daoexamples.ear文件。這個應(yīng)用程序?qū)⑦\(yùn)行在任何與J2EE兼容的應(yīng)用程序服務(wù)器。