基于Spring声明式事务的分布式事务管理


开发传统应用遇到的最大问题就是多数据源的事务一致性问题, 下面介绍一种常用的分布式事务处理方式的使用

开发环境:Eclipse+Java1.7 + Tomcat7, 另外使用到的开源框架 Spring3.X

提到分布式事务大家首先想到的肯定是jta,我们用到的就是基于jta的一种实现atomikos,下面就介绍一下他的使用

1、依赖的jar

Maven依赖代码

                <!--atomikos 分布式事务依赖 -->
                <dependency>
   <groupId>com.atomikos</groupId>
   <artifactId>transactions-jta</artifactId>
   <version>${atomikos.version}</version>
   <optional>true</optional>
  </dependency>
  <dependency>
   <groupId>com.atomikos</groupId>
   <artifactId>transactions-jdbc</artifactId>
   <version>${atomikos.version}</version>
   <optional>true</optional>
  </dependency>
  <dependency>
   <groupId>javax.transaction</groupId>
   <artifactId>jta</artifactId>
   <version>${jta.version}</version>
   <optional>true</optional>
  </dependency>
<!--数据源的连接池-->
  <dependency>
   <groupId>com.alibaba</groupId>
   <artifactId>druid</artifactId>
   <version>1.0.9</version>
  </dependency>

2、 配置数据源

数据源连接池,必须是支持XA标准的连接池,什么是XA标准呢?

XA协议由Tuxedo首先提出的,并交给X/Open组织,作为资源管理器(数据库)与事务管理器的接口标准。目前,Oracle、Informix、DB2和Sybase等各大数据库厂家都提供对XA的支持。XA协议采用两阶段提交方式来管理分布式事务。XA接口提供资源管理器与事务管理器之间进行通信的标准接口。XA协议包括两套函数,以xa_开头的及以ax_开头的。
以下的函数使事务管理器可以对资源管理器进行的操作:
1)xa_open,xa_close:建立和关闭与资源管理器的连接。
2)xa_start,xa_end:开始和结束一个本地事务。
3)xa_prepare,xa_commit,xa_rollback:预提交、提交和回滚一个本地事务。
4)xa_recover:回滚一个已进行预提交的事务。
5)ax_开头的函数使资源管理器可以动态地在事务管理器中进行注册,并可以对XID(TRANSACTION IDS)进行操作。
6)ax_reg,ax_unreg;允许一个资源管理器在一个TMS(TRANSACTION MANAGER SERVER)中动态注册或撤消注册。

Java代码

<!--dataSource-->
    <bean id="dataSource1" class="com.alibaba.druid.pool.xa.DruidXADataSource" init-method="init" destroy-method="close">
        <!-- 基本属性 url、user、password -->
        <property name="url" value="${jdbc.url}" />
        <property name="username" value="${jdbc.username}" />
        <property name="password" value="${jdbc.password}" />
        <!-- 配置初始化大小、最小、最大 -->
        <property name="initialSize" value="${jdbc.initialSize}" />
        <property name="minIdle" value="${jdbc.minIdle}" />
        <property name="maxIdle" value="${jdbc.maxIdle}" />
        <property name="maxActive" value="${jdbc.maxActive}" />
        <!-- 配置获取连接等待超时的时间 -->
        <property name="maxWait" value="${jdbc.maxWait}"/>
        <!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
        <property name="timeBetweenEvictionRunsMillis" value="${jdbc.timeBetweenEvictionRunsMillis}" />
        <!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->
        <property name="minEvictableIdleTimeMillis" value="${jdbc.minEvictableIdleTimeMillis}" />
        <property name="validationQuery" value="${jdbc.validationQuery}" />
        <property name="testWhileIdle" value="${jdbc.testWhileIdle}" />
        <property name="testOnBorrow" value="${jdbc.testOnBorrow}" />
        <property name="testOnReturn" value="${jdbc.testOnReturn}" />
        <property name="filters" value="${jdbc.filters}" />
    </bean>

 

<bean id="dataSource2" class="com.alibaba.druid.pool.xa.DruidXADataSource" init-method="init" destroy-method="close">
        <!-- 基本属性 url、user、password -->
        <property name="url" value="${jdbc1.url}" />
        <property name="username" value="${jdbc1.username}" />
        <property name="password" value="${jdbc1.password}" />
        <!-- 配置初始化大小、最小、最大 -->
        <property name="initialSize" value="${jdbc.initialSize}" />
        <property name="minIdle" value="${jdbc.minIdle}" />
        <property name="maxIdle" value="${jdbc.maxIdle}" />
        <property name="maxActive" value="${jdbc.maxActive}" />
        <!-- 配置获取连接等待超时的时间 -->
        <property name="maxWait" value="${jdbc.maxWait}"/>
        <!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
        <property name="timeBetweenEvictionRunsMillis" value="${jdbc.timeBetweenEvictionRunsMillis}" />
        <!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->
        <property name="minEvictableIdleTimeMillis" value="${jdbc.minEvictableIdleTimeMillis}" />
        <property name="validationQuery" value="${jdbc.validationQuery}" />
        <property name="testWhileIdle" value="${jdbc.testWhileIdle}" />
        <property name="testOnBorrow" value="${jdbc.testOnBorrow}" />
        <property name="testOnReturn" value="${jdbc.testOnReturn}" />
        <property name="filters" value="${jdbc.filters}" />
    </bean>

 

3、强数据源指定到AtomikosDataSourceBean中,再有该类的对象向DAO层提供数据源服务

配置如下

datasource1

<bean id="xaDataSource1" class="com.atomikos.jdbc.AtomikosDataSourceBean"  destroy-method="close"> 
      <property name="xaDataSource" ref="dataSource1"/>
      <property name="uniqueResourceName" value="dataSource1"/> 
    <property name="xaDataSourceClassName" value="com.alibaba.druid.pool.xa.DruidXADataSource"/> 
    <property name="xaProperties"> 
          <props> 
              <prop key="serverName">test</prop> 
                          <prop key="portNumber">test</prop> 
                          <prop key="databaseName">test</prop> 
              <prop key="user">test</prop> 
              <prop key="password">test</prop> 
      </props>
    </property> 
 </bean>

dataSource2

<bean id="xaDataSource2" class="com.atomikos.jdbc.AtomikosDataSourceBean"  destroy-method="close"> 
      <property name="xaDataSource" ref="dataSource2"/>
      <property name="uniqueResourceName" value="dataSource2"/> 
    <property name="xaDataSourceClassName" value="com.alibaba.druid.pool.xa.DruidXADataSource"/> 
    <property name="xaProperties"> 
          <props> 
              <prop key="serverName">test</prop> 
                          <prop key="portNumber">test</prop> 
                          <prop key="databaseName">test</prop> 
              <prop key="user">test</prop> 
              <prop key="password">test</prop> 
      </props>
    </property> 
 </bean>

3、将数据源托管到TranscationManager

<bean id="atomikoSUSErTransaction"
        class="com.atomikos.icatch.jta.UserTransactionImp">
        <description>UserTransactionImp</description>
        <property name="transactionTimeout" value="300"/>
</bean>

<bean id="atomikosTransactionManager"  class="com.atomikos.icatch.jta.UserTransactionManager"
        init-method="init" destroy-method="close">
        <description>UserTransactionManager</description>
        <property name="forceShutdown">
            <value>true</value>
        </property>
</bean>

<bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager">
        <description>JtaTransactionManager</description>
        <property name="transactionManager">
            <ref bean="atomikosTransactionManager" />
        </property>
        <property name="userTransaction">
            <ref bean="atomikosUserTransaction" />
        </property>
        <property name="allowCustomIsolationLevels" value="true"/>
</bean>

4、定义切面

<tx:advice id="txAdvice" transaction-manager="transactionManager">
 <tx:attributes>
  <tx:method name="do*" propagation="REQUIRED" rollback-for="Throwable, Exception, RuntimeException" />
  <tx:method name="query*" propagation="NOT_SUPPORTED" />
  <tx:method name="load*" propagation="NOT_SUPPORTED" />
  <tx:method name="find*" propagation="NOT_SUPPORTED" />
  <tx:method name="*" read-only="true" propagation="SUPPORTS" />
 </tx:attributes>
</tx:advice>
<aop:config>
 <aop:pointcut expression="execution(* com.jl.net..service.*.*(..))" id="pointCut" />
 <aop:advisor pointcut-ref="pointCut" advice-ref="txAdvice" />
 <aop:aspect ref="serviceInterceptor" order="2">
  <aop:around method="doAround" pointcut-ref="pointCut" />
 </aop:aspect>
</aop:config>

5、 jta.properties (路径跟log4j.properties一致就好)

这个文件不配置系统也能启动,因为有默认配置,个人建议配置一下。详细配置

com.atomikos.icatch.console_file_name = xam.out
com.atomikos.icatch.log_base_name = xamlog.log
com.atomikos.icatch.service = com.atomikos.icatch.standalone.UserTransactionServiceFactory

Spring中如何配置Hibernate事务

Struts2整合Spring方法及原理

基于 Spring 设计并实现 RESTful Web Services

Spring-3.2.4 + Quartz-2.2.0集成实例

使用 Spring 进行单元测试

运用Spring注解实现Netty服务器端UDP应用程序

Spring 3.x 企业应用开发实战 PDF完整高清扫描版+源代码

Spring 的详细介绍:请点这里
Spring 的下载地址:请点这里

本文永久更新链接地址

相关内容