现在的位置: 首页 > 综合 > 正文

Spring全局事务之WebLogicJtaTransactionManager

2013年08月05日 ⁄ 综合 ⁄ 共 8696字 ⁄ 字号 评论关闭

全局事务是指在一个事务中涉及到几个事务参入者,这些事务参入者可以是我们常见的数据库操作,消息(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.
对于二和三的应用,在实际中应该用的很少了.

【上篇】
【下篇】

抱歉!评论已关闭.