Spring JTA應用之JOTM配置
原文地址:http://tom-duan.javaeye.com/blog/147594JOTM(Java Open Transaction Manager)是ObjectWeb的一個開源JTA實現,本身也是開源應用程序服務器JOnAS(Java Open Application Server)的一部分,為其提供JTA分布式事務的功能。Spring對JOTM提供了較好的支持,提供了一個org.springframework.transaction.jta.JotmFactoryBean的支持類,在Spring2.0中也包含了JOTM相關的一些library。
jotm的下載地址為http://jotm.objectweb.org,最新版本為2.0.10.
下載完成后解壓縮,然后打開jotm下面conf文件夾,拷貝carol.properties文件到classpath中,并修改這個文件如下
carol.properties
- #?do?not?use?CAROL?JNDI?wrapper??? ??
- carol.start.jndi=false??? ??
- ??? ??
- #?do?not?start?a?name?server??? ??
- carol.start.ns=false??? ??
- ??? ??
- #?Naming?Factory ??
- carol.jndi.java.naming.factory.url.pkgs=org.apache.naming??
# do not use CAROL JNDI wrapper carol.start.jndi=false # do not start a name server carol.start.ns=false # Naming Factory carol.jndi.java.naming.factory.url.pkgs=org.apache.naming
上面配置文件的目的是不使用JNDI的方式來加載JOTM的配置,當然也可以根據需要選擇其它的一些配置,例如JTOM所提供的默認配置。
然后開始在Spring上下文中配置JOTM,在classpath中建立一個ApplicationContext-jotm.xml,配置如下
ApplicationContext-jotm.xml
- <?xml?version="1.0"?encoding="UTF-8"?> ??
- <beans?xmlns="http://www.springframework.org/schema/beans"??
- ????xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"??
- ????xsi:schemaLocation="http://www.springframework.org/schema/beans?http://www.springframework.org/schema/beans/spring-beans-2.0.xsd"> ??
- ??
- ????<bean?id="jotm"?class="org.springframework.transaction.jta.JotmFactoryBean"/> ??
- ???? ??
- ????<bean?id="txManager"?class="org.springframework.transaction.jta.JtaTransactionManager"> ??
- ????????<property?name="userTransaction"?ref="jotm"?/> ??
- ????</bean> ??
- ??
- ????<bean?id="ds1"?class="org.enhydra.jdbc.pool.StandardXAPoolDataSource"?destroy-method="shutdown"> ??
- ????????<property?name="dataSource"> ??
- ????????????<bean?class="org.enhydra.jdbc.standard.StandardXADataSource"?destroy-method="shutdown"> ??
- ????????????????<property?name="transactionManager"?ref="jotm"?/> ??
- ????????????????<property?name="driverName"?value="com.mysql.jdbc.Driver"?/> ??
- ????????????????<property?name="url"?value="jdbc:MySQL://localhost:3306/test"?/> ??
- ????????????</bean> ??
- ????????</property> ??
- ????????<property?name="user"?value="root"?/> ??
- ????????<property?name="password"?value="admin"?/> ??
- ????</bean> ??
- ???? ??
- ????<bean?id="ds2"?class="org.enhydra.jdbc.pool.StandardXAPoolDataSource"?destroy-method="shutdown"> ??
- ????????<property?name="dataSource"> ??
- ????????????<bean?class="org.enhydra.jdbc.standard.StandardXADataSource"?destroy-method="shutdown"> ??
- ????????????????<property?name="transactionManager"?ref="jotm"?/> ??
- ????????????????<property?name="driverName"?value="com.mysql.jdbc.Driver"?/> ??
- ????????????????<property?name="url"?value="jdbc:MySQL://localhost:3306/test2"?/> ??
- ????????????</bean> ??
- ????????</property> ??
- ????????<property?name="user"?value="root"?/> ??
- ????????<property?name="password"?value="admin"?/> ??
- ????</bean> ??
- ??
- ????<bean?id="template1"?class="org.springframework.jdbc.core.JdbcTemplate"> ??
- ????????<property?name="dataSource"?ref="ds1"?/> ??
- ????</bean> ??
- ???? ??
- ????<bean?id="template2"?class="org.springframework.jdbc.core.JdbcTemplate"> ??
- ????????<property?name="dataSource"?ref="ds2"?/> ??
- ????</bean> ??
- ???? ??
- ????<bean?id="dao1"?class="com.xa.dao.UserDao1"> ??
- ????????<property?name="jdbcTemplate"> ??
- ????????????<ref?bean="template1"></ref> ??
- ????????</property> ??
- ????</bean> ??
- ???? ??
- ????<bean?id="dao2"?class="com.xa.dao.UserDao2"> ??
- ????????<property?name="jdbcTemplate"> ??
- ????????????<ref?bean="template2"></ref> ??
- ????????</property> ??
- ????</bean> ??
- ???? ??
- ????<bean?id="userServiceTarget"?class="com.xa.service.UserServiceImpl"> ??
- ????????<property?name="dao1"?ref="dao1"/> ??
- ????????<property?name="dao2"?ref="dao2"/> ??
- ????</bean> ??
- ??
- ??
- ????<bean?id="userTest"?class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">???? ??
- ????????<property?name="transactionManager"> ??
- ????????????<ref?bean="txManager"/> ??
- ????????</property>???? ??
- ????????<property?name="target"> ??
- ????????????<ref?bean="userServiceTarget"/> ??
- ????????</property> ??
- ????????<property?name="transactionAttributes">???????? ??
- ????????????<props> ??
- ????????????????<prop?key="insert*">PROPAGATION_REQUIRED,-Exception</prop>???????????? ??
- ????????????</props> ??
- ????????</property> ??
- ????</bean> ??
- </beans>??
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd"> <bean id="jotm" class="org.springframework.transaction.jta.JotmFactoryBean"/> <bean id="txManager" class="org.springframework.transaction.jta.JtaTransactionManager"> <property name="userTransaction" ref="jotm" /> </bean> <bean id="ds1" class="org.enhydra.jdbc.pool.StandardXAPoolDataSource" destroy-method="shutdown"> <property name="dataSource"> <bean class="org.enhydra.jdbc.standard.StandardXADataSource" destroy-method="shutdown"> <property name="transactionManager" ref="jotm" /> <property name="driverName" value="com.mysql.jdbc.Driver" /> <property name="url" value="jdbc:MySQL://localhost:3306/test" /> </bean> </property> <property name="user" value="root" /> <property name="password" value="admin" /> </bean> <bean id="ds2" class="org.enhydra.jdbc.pool.StandardXAPoolDataSource" destroy-method="shutdown"> <property name="dataSource"> <bean class="org.enhydra.jdbc.standard.StandardXADataSource" destroy-method="shutdown"> <property name="transactionManager" ref="jotm" /> <property name="driverName" value="com.mysql.jdbc.Driver" /> <property name="url" value="jdbc:MySQL://localhost:3306/test2" /> </bean> </property> <property name="user" value="root" /> <property name="password" value="admin" /> </bean> <bean id="template1" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="ds1" /> </bean> <bean id="template2" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="ds2" /> </bean> <bean id="dao1" class="com.xa.dao.UserDao1"> <property name="jdbcTemplate"> <ref bean="template1"></ref> </property> </bean> <bean id="dao2" class="com.xa.dao.UserDao2"> <property name="jdbcTemplate"> <ref bean="template2"></ref> </property> </bean> <bean id="userServiceTarget" class="com.xa.service.UserServiceImpl"> <property name="dao1" ref="dao1"/> <property name="dao2" ref="dao2"/> </bean> <bean id="userTest" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"> <property name="transactionManager"> <ref bean="txManager"/> </property> <property name="target"> <ref bean="userServiceTarget"/> </property> <property name="transactionAttributes"> <props> <prop key="insert*">PROPAGATION_REQUIRED,-Exception</prop> </props> </property> </bean> </beans>
上面是一個完整的Spring上下文配置,可以看第一個bean “jotm”,實際上引用了Spring內部所提供的對JOTM支持的工廠類,參考下面的配置代碼段
- <bean?id="jotm"?class="org.springframework.transaction.jta.JotmFactoryBean"/>??
<bean id="jotm" class="org.springframework.transaction.jta.JotmFactoryBean"/>
隨后,配置了JTA事務管理器,并且在管理器中使用上面所配置的jotm,如下面的代碼
- <bean?id="txManager"?class="org.springframework.transaction.jta.JtaTransactionManager"> ??
- ????<property?name="userTransaction"?ref="jotm"?/> ??
- </bean>??
<bean id="txManager" class="org.springframework.transaction.jta.JtaTransactionManager"> <property name="userTransaction" ref="jotm" /> </bean>
再接下來就是配置多個數據源了,使用jotm提供的org.enhydra.jdbc.pool.StandardXAPoolDataSource類,根據類名可以明確地看出它是用以配置多個數據源的啦,配置的代碼如下
- <bean?id="ds1"?class="org.enhydra.jdbc.pool.StandardXAPoolDataSource"?destroy-method="shutdown"> ??
- ????????<property?name="dataSource"> ??
- ????????????<bean?class="org.enhydra.jdbc.standard.StandardXADataSource"?destroy-method="shutdown"> ??
- ????????????????<property?name="transactionManager"?ref="jotm"?/> ??
- ????????????????<property?name="driverName"?value="com.mysql.jdbc.Driver"?/> ??
- ????????????????<property?name="url"?value="jdbc:MySQL://localhost:3306/test"?/> ??
- ????????????</bean> ??
- ????????</property> ??
- ????????<property?name="user"?value="root"?/> ??
- ????????<property?name="password"?value="admin"?/> ??
- ????</bean> ??
- ???? ??
- ????<bean?id="ds2"?class="org.enhydra.jdbc.pool.StandardXAPoolDataSource"?destroy-method="shutdown"> ??
- ????????<property?name="dataSource"> ??
- ????????????<bean?class="org.enhydra.jdbc.standard.StandardXADataSource"?destroy-method="shutdown"> ??
- ????????????????<property?name="transactionManager"?ref="jotm"?/> ??
- ????????????????<property?name="driverName"?value="com.mysql.jdbc.Driver"?/> ??
- ????????????????<property?name="url"?value="jdbc:MySQL://localhost:3306/test2"?/> ??
- ????????????</bean> ??
- ????????</property> ??
- ????????<property?name="user"?value="root"?/> ??
- ????????<property?name="password"?value="admin"?/> ??
- ????</bean>??
<bean id="ds1" class="org.enhydra.jdbc.pool.StandardXAPoolDataSource" destroy-method="shutdown"> <property name="dataSource"> <bean class="org.enhydra.jdbc.standard.StandardXADataSource" destroy-method="shutdown"> <property name="transactionManager" ref="jotm" /> <property name="driverName" value="com.mysql.jdbc.Driver" /> <property name="url" value="jdbc:MySQL://localhost:3306/test" /> </bean> </property> <property name="user" value="root" /> <property name="password" value="admin" /> </bean> <bean id="ds2" class="org.enhydra.jdbc.pool.StandardXAPoolDataSource" destroy-method="shutdown"> <property name="dataSource"> <bean class="org.enhydra.jdbc.standard.StandardXADataSource" destroy-method="shutdown"> <property name="transactionManager" ref="jotm" /> <property name="driverName" value="com.mysql.jdbc.Driver" /> <property name="url" value="jdbc:MySQL://localhost:3306/test2" /> </bean> </property> <property name="user" value="root" /> <property name="password" value="admin" /> </bean>
這里配置的兩個數據源都連接到本地的mysql,實際上可以連接到不同的db server和不同類型的數據庫,已經經過測試,這里為了方便,在本地建立了兩個不同的數據庫(test,test2)做測試。
隨后的配置基本上和普通的Spring上下文配置相同了,根據不同的數據源配置兩個jdbcTemplate,兩個dao分別引用不同的jdbcTemplate, 將兩個dao注入到UserService中, 最后將service納入事務管理,并在事務代理配置中配置回滾規則,意思為如遇異常,則強制回滾內容。配置如下所示
- <property?name="transactionAttributes">???????? ??
- ????<props> ??
- ????????<prop?key="insert*">PROPAGATION_REQUIRED,-Exception</prop>???????????? ??
- ????</props> ??
- </property>??
<property name="transactionAttributes"> <props> <prop key="insert*">PROPAGATION_REQUIRED,-Exception</prop> </props> </property>
這樣,一個使用JOTM JTA事務的簡單應用算大致成型了,最后,寫一個JUnit,來測試一下結果
TestXa.java
- package?com.xa; ??
- ??
- import?org.springframework.context.ApplicationContext; ??
- import?org.springframework.test.AbstractDependencyInjectionSpringContextTests; ??
- ??
- import?com.xa.service.UserService; ??
- ??
- public?class?TestXa?extends?AbstractDependencyInjectionSpringContextTests ??
- { ??
- ????protected?String[]?getConfigLocations()?{ ??
- ????????return?new?String[]?{?"classpath:ApplicationContext-jotm.xml"?}; ??
- ????} ??
- ??
- ????public?void?testInsertBothDatabase()?{ ??
- ????????ApplicationContext?ctx?=?this.getApplicationContext(); ??
- ????????UserService?ut?=?(UserService)ctx.getBean("userTest"); ??
- ????????try?{ ??
- ????????????ut.insertBothDatabase("1",?null); ??
- ????????} ??
- ????????catch?(Exception?e)?{ ??
- ????????????e.printStackTrace(); ??
- ????????} ??
- ????} ??
- }??
package com.xa; import org.springframework.context.ApplicationContext; import org.springframework.test.AbstractDependencyInjectionSpringContextTests; import com.xa.service.UserService; public class TestXa extends AbstractDependencyInjectionSpringContextTests { protected String[] getConfigLocations() { return new String[] { "classpath:ApplicationContext-jotm.xml" }; } public void testInsertBothDatabase() { ApplicationContext ctx = this.getApplicationContext(); UserService ut = (UserService)ctx.getBean("userTest"); try { ut.insertBothDatabase("1", null); } catch (Exception e) { e.printStackTrace(); } } }
在test中,調用了UserService的insertBothDatabase方法,有兩個參數,userId和UserName,另外在方法的實現中調用了兩個使用不同數據源dao,分別向兩個不同的數據庫插入輸入,而test2數據庫的xa_test表中,name字段是不允許為空的,因此,在插入test2數據庫時會失敗.
運行這個test,然后察看數據庫結果:),test和test2數據庫中都沒有插入成功,看serviceImpl中的代碼可以知道,邏輯上dao1會先于dao2執行,但是由于JTA事務,在dao2插入數據出現異常時整個事務被回滾,由于事務被配置在service層,dao1和dao2都被納入一個事務進行管理,呵呵。修改一下方法的參數,修改為
- ut.insertBothDatabase("1",?"name1");??
ut.insertBothDatabase("1", "name1");
然后再試試test看數據庫結果,如何?
posted on 2009-12-09 16:34 飛熊 閱讀(1262) 評論(0) 編輯 收藏 所屬分類: JTA