全局事务是指在一个事务中涉及到几个事务参入者,这些事务参入者可以是我们常见的数据库操作,消息(MQ)操作等等.如同时进行下面的操作,比如"转账"操作发生在两个数据库:
1,从数据库A的的表中将某个帐号的余额减少.
2从数据库B的的表中将某个帐号的余额增加.
3,提交在数据库A中的操作.
4,提交在数据库B中的操作.
通常单个数据库只能保证本数据库的事务要么提交要么回滚,当涉及的事务跨数据库的时候,就需要一个在两个数据库之间进行事务"协调"的事务管理器了,而数据库也需要为参入这种"协调"处理实现特殊协议,叫做XA或者两阶段提交协议.Java对这种全局事务管理是通过JTA规范来做的,通常就是begin,commit或者rollback的形式,本文通过示例简单介绍一下Spring利用Weblogic提供的JTA事务管理器管理全局事务.
*注意,如果用Spring的事务声明,WebLogicJtaTransactionManager只能管理在Weblogic通过DataSource配置的数据库操作,或者在Weblogic上配置的JMS.(但是也没有找到官方文档这样明确的说明过,本人在使用的过程中曾经尝试将不是从Weblogic上配置的XADataSource中进行的数据库操作,以及通过ActiveMQ的ActiveMQXAConnectionFactory操作JSM声明为Spring利用WebLogicJtaTransactionManager,发现行不通.如果有这种需求的话,可以使用atomikos或者jotm来实现)
下面简单介绍一下Spring和WebLogicJtaTransactionManager结合使用的一些应用:一,用到Spring的事务声明,应用在Weblogic容器中运行,二,用到Spring的事务声明,应用不是在Weblogic容器中运行,三,没有用到Spring的事务声明.
一应用在Weblogic容器中运行:
1,在Weblogic上配置连接到两个不同数据库的DataSource,需要DataSource支持XA.
2,在Spring中配置如下:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:jee="http://www.springframework.org/schema/jee" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd"> <bean id="appContextProvider" class="com.test.spring.tx.util.ApplicationContextProvider" /> <bean id="db1jdbcDAO" class="com.test.spring.tx.xawl.DB1jdbcDAO"> <property name="dataSource" ref="dataSource1" /> </bean> <bean id="db2jdbcDAO" class="com.test.spring.tx.xawl.DB2jdbcDAO"> <property name="dataSource" ref="dataSource2" /> </bean> <bean id="dataSource1" class="org.springframework.jndi.JndiObjectFactoryBean"> <property name="jndiName"> <value>ds89</value> </property> </bean> <bean id="dataSource2" class="org.springframework.jndi.JndiObjectFactoryBean"> <property name="jndiName"> <value>DSORCL</value> </property> </bean> <bean id="buzSingleService" class="com.test.spring.tx.xawl.BuzSingleService"> <property name="db1jdbcDAO" ref="db1jdbcDAO" /> <property name="db2jdbcDAO" ref="db2jdbcDAO" /> </bean> <tx:advice id="txAdvice" transaction-manager="transactionManager"> <tx:attributes> <tx:method name="*" propagation="REQUIRED"/> </tx:attributes> </tx:advice> <bean id="transactionManager" class="org.springframework.transaction.jta.WebLogicJtaTransactionManager" > <property name="transactionManagerName" value="javax.transaction.TransactionManager" /> </bean> <!-- you can also use JtaTransactionManager <bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager" > <property name="userTransactionName"> <value>weblogic/transaction/UserTransaction</value> </property> </bean> --> <aop:config> <aop:pointcut id="serviceOperation" expression="execution(* com.test.spring.tx.xawl.*Service*.*(..))" /> <aop:advisor advice-ref="txAdvice" pointcut-ref="serviceOperation" /> </aop:config> </beans>
3,Java代码:
public class DB1jdbcDAO { private JdbcTemplate jdbcTemplate; public void setDataSource(DataSource dataSource) { this.jdbcTemplate = new JdbcTemplate(dataSource); } public void testInsert(int id, String val) { this.jdbcTemplate.update("insert into A (ID, VAL) values (?, ?)", id, val); } public class BuzSingleService { DB1jdbcDAO db1jdbcDAO; DB2jdbcDAO db2jdbcDAO; public void testTX1() throws Exception { db1jdbcDAO.testInsert(0, "db1jdbcDAO val0"); db2jdbcDAO.testInsert(0, "db2jdbcDAO val0"); }
4,示例需要发布到Weblogic中,访问BuzSingleService 的代码在一个servlet中如下:
public class XawlTestServlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException { System.out.println("accessed:" + new Date()); PrintWriter pw = response.getWriter(); BuzSingleService serv = (BuzSingleService) ApplicationContextProvider.getApplicationContext().getBean( "buzSingleService"); try { serv.testTX1(); } catch (Exception e) { e.printStackTrace(); pw.println(e.getMessage()); } pw.println("Http response from XawlTestServlet"); } public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException { doGet(request, response); } } public class ApplicationContextProvider implements ApplicationContextAware { private static ApplicationContext ctx; public static ApplicationContext getApplicationContext() { return ctx; } public void setApplicationContext(ApplicationContext appCtx) throws BeansException { ApplicationContextProvider.ctx=appCtx; } }
5,配置web.xml如下:
<?xml version="1.0" encoding="UTF-8"?> <web-app id="WebApp_ID" version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"> <display-name> prjSptx</display-name> <welcome-file-list> <welcome-file>index.html</welcome-file> </welcome-file-list> <context-param> <param-name>contextConfigLocation</param-name> <param-value> classpath:config/xawlAppcontext.xml </param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <servlet> <servlet-name>xawl</servlet-name> <servlet-class> com.test.spring.tx.servlet.XawlTestServlet </servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>xawl</servlet-name> <url-pattern>*.xawl</url-pattern> </servlet-mapping> </web-app>
6,在浏览器输入 http://localhost:7001/springWeb/xx.xawl,在两个数据库中正常插入数据.,如果将testTX1改为如下,两个数据库中都不会有数据插入.
db1jdbcDAO.testInsert(0, "db1jdbcDAO val0"); db2jdbcDAO.testInsert(0, "db2jdbcDAO val0"); String nullStr = null; nullStr.length();
对于声明型事务的一些提交回滚控制特性的其他介绍请参照 http://blog.csdn.net/kkdelta/article/details/7258050
二,如果代码不是在Weblogic容器中运行,如在standalone的main方法中,需要将weblogic.jar和xbean.jar加入到运行的classpath,Spring的配置如下(加入JNDIlookup的支持):
<bean id="db1jdbcDAO" class="com.test.spring.tx.xawl.DB1jdbcDAO"> <property name="dataSource" ref="dataSource1" /> </bean> <bean id="db2jdbcDAO" class="com.test.spring.tx.xawl.DB2jdbcDAO"> <property name="dataSource" ref="dataSource2" /> </bean> <bean id="dataSource1" class="org.springframework.jndi.JndiObjectFactoryBean"> <property name="jndiTemplate"> <ref local="jndiTemplate" /> </property> <property name="jndiName"> <value>ds89</value> </property> </bean> <bean id="dataSource2" class="org.springframework.jndi.JndiObjectFactoryBean"> <property name="jndiTemplate"> <ref local="jndiTemplate" /> </property> <property name="jndiName"> <value>DSORCL</value> </property> </bean> <bean id="jndiTemplate" class="org.springframework.jndi.JndiTemplate"> <property name="environment"> <props> <prop key="java.naming.factory.initial"> weblogic.jndi.WLInitialContextFactory </prop> <prop key="java.naming.provider.url">t3://localhost:7001</prop> </props> </property> </bean> <bean id="buzSingleService" class="com.test.spring.tx.xawl.BuzSingleService"> <property name="db1jdbcDAO" ref="db1jdbcDAO" /> <property name="db2jdbcDAO" ref="db2jdbcDAO" /> </bean> <tx:advice id="txAdvice" transaction-manager="transactionManager"> <tx:attributes> <tx:method name="*" propagation="REQUIRED"/> </tx:attributes> </tx:advice> <bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager" > <property name="jndiTemplate"> <ref local="jndiTemplate" /> </property> <property name="userTransactionName"> <value>weblogic/transaction/UserTransaction</value> </property> </bean> <aop:config> <aop:pointcut id="serviceOperation" expression="execution(* com.test.spring.tx.xawl.*Service*.*(..))" /> <aop:advisor advice-ref="txAdvice" pointcut-ref="serviceOperation" /> </aop:config>
JAVA main方法代码:
ApplicationContext ctx = new ClassPathXmlApplicationContext("config/xawlStandaloneAppcontext.xml"); BuzSingleService serv =(BuzSingleService)ctx.getBean("buzSingleService"); try { serv.testTX1();
*在使用过程中遇到的一些问题:
a,如果将Spring配置为default-autowire="byName",会有如下异常:
java.lang.IllegalStateException: Cannot convert value of type [org.springframework.transaction.jta.JtaTransactionManager] to required type [javax.transaction.TransactionManager] for property 'transactionManager': no matching editors or conversion strategy found at org.springframework.beans.TypeConverterDelegate.convertIfNecessary(TypeConverterDelegate.java:289)
b,TransactionAwareDataSourceProxy让人很迷惑,开始以为是可以让不是在Weblogic上配置的DataSource也能自动参入到事务中,其实不是.
Spring文档的介绍是这个类可以让历史遗留代码的Datasource能够参入到事务中,就像是从JNDI得到Datasource一样,实际上不是.(一直没有弄明白)
三,DataSource不是在Weblogic上配置的话,Spring就只能起到一个提供Bean工厂的作用,为得到JTA transaction manager提供方便.
关于使用JTA编程的方式可以参照http://blog.csdn.net/kkdelta/article/details/5579142.
对于二和三的应用,在实际中应该用的很少了.