开发传统应用遇到的最大问题就是多数据源的事务一致性问题, 下面介绍一种常用的分布式事务处理方式的使用
开发环境: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标准呢?
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="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