项目进行读写分离及分库分表,在一个业务中,在一个事务中处理时候将切换多个数据源,需要保证同一事务多个数据源数据的一致性。此处使用atomikos来实现:最后附源码:
1:spring3.0之后不再支持jtom[jta]了,第三方开源软件atomikos(http://www.atomikos.com/)来实现.
2:org.springframework.transaction.jta.JotmFactoryBean类,spring-tx-2.5.6.jar中有此类,spring-tx-3.0.0.RELEASE.jar之后没有此类。
3:atomikos事务控制框架,其中看到有3种数据源,分别是,SimpleDataSourceBean,AtomikosDataSourceBean,AtomikosNonXADataSourceBean。
a:SimpleDataSourceBean: 这个是最简单地数据源配置,需要配置XA驱动。
b:AtomikosDataSourceBean: 分布式数据源,Atomikos实现的数据源,需要配置XA驱动,推荐此配置,可以配置连接池的信息。
c:AtomikosNonXADataSourceBean: 非分布式数据源,该数据源配置需要普通JDBC的驱动,可以配置连接池:
4:Atomikos支持XA(全局事务)和NON-XA(非全局事务),NON-XA[nonxadatasource]效率高于XA.XA事务往往是包括多个数据源的全局事务,非XA是单个数据源的.
5:XA连接是一个JTA事务中的参与者。XA连接不支持JDBC的自动提交特性。也就是说应用程序不必在xadatasource[XA]连接上调用java.sql.Connection.commit()或java.sql.Connection.rollback();而应用程序应该使用UserTransaction.begin(),UserTransaction.commit()和UserTransaction.rollback().
看看pom.xml依赖:
<!-- transaction --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> <version>4.2.5.RELEASE</version> </dependency> <dependency> <groupId>javax.transaction</groupId> <artifactId>jta</artifactId> <version>1.1</version> </dependency> <dependency> <groupId>com.atomikos</groupId> <artifactId>atomikos-util</artifactId> <version>4.0.2</version> </dependency> <dependency> <groupId>com.atomikos</groupId> <artifactId>transactions</artifactId> <version>4.0.2</version> </dependency> <dependency> <groupId>com.atomikos</groupId> <artifactId>transactions-jta</artifactId> <version>4.0.2</version> </dependency> <dependency> <groupId>com.atomikos</groupId> <artifactId>transactions-jdbc</artifactId> <version>4.0.2</version> </dependency> <dependency> <groupId>com.atomikos</groupId> <artifactId>transactions-api</artifactId> <version>4.0.2</version> </dependency> <dependency> <groupId>cglib</groupId> <artifactId>cglib-nodep</artifactId> <version>3.2.2</version> </dependency>1:AtomikosDataSourceBean[XA(全局事务)]数据源配置datasource-context.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" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd" default-lazy-init="true"> <description>配置主-从数据源信息</description> <!-- com.atomikos.jdbc.nonxa.AtomikosNonXADataSourceBean --> <bean id="abstractXADataSource" class="com.atomikos.jdbc.AtomikosDataSourceBean" init-method="init" destroy-method="close" abstract="true"> <property name="xaDataSourceClassName" value="${jdbc.xaDataSourceClassName}"/> <!-- SQLErrorCodes loaded: [DB2, Derby, H2, HSQL, Informix, MS-SQL, MySQL, Oracle, PostgreSQL, Sybase, Hana] --> <property name="poolSize" value="10" /> <property name="minPoolSize" value="10"/> <property name="maxPoolSize" value="30"/> <property name="borrowConnectionTimeout" value="60"/> <property name="reapTimeout" value="20"/> <property name="maxIdleTime" value="60"/> <property name="maintenanceInterval" value="60"/> <property name="loginTimeout" value="60"/> <property name="testQuery" value="${validationQuery}"/> </bean> <bean id="masterDataSource" parent="abstractXADataSource"> <property name="uniqueResourceName" value="masterDB" /> <property name="xaProperties"> <props> <prop key="driverClassName">${jdbc.driverClassName}</prop> <prop key="url">${master.jdbc.url}</prop> <prop key="password">${jdbc.password}</prop> <!-- <prop key="user">${jdbc.username}</prop> --> <!-- mysql --> <prop key="username">${jdbc.username}</prop> <!-- durid --> <prop key="initialSize">0</prop> <prop key="maxActive">20</prop> <!-- 若不配置则代码执行"{dataSource-1} inited"此处停止 --> <prop key="minIdle">0</prop> <prop key="maxWait">60000</prop> <prop key="validationQuery">${validationQuery}</prop> <prop key="testOnBorrow">false</prop> <prop key="testOnReturn">false</prop> <prop key="testWhileIdle">true</prop> <prop key="removeAbandoned">true</prop> <prop key="removeAbandonedTimeout">1800</prop> <prop key="logAbandoned">true</prop> <prop key="filters">mergeStat</prop> </props> </property> </bean> <bean id="slaveDataSource" parent="abstractXADataSource"> <property name="uniqueResourceName" value="slaveDB" /> <property name="xaProperties"> <props> <prop key="driverClassName">${jdbc.driverClassName}</prop> <prop key="url">${slave.jdbc.url}</prop> <prop key="password">${jdbc.password}</prop> <!-- <prop key="user">${jdbc.username}</prop> --> <prop key="username">${jdbc.username}</prop> <prop key="initialSize">0</prop> <prop key="maxActive">20</prop> <prop key="minIdle">0</prop> <prop key="maxWait">60000</prop> <prop key="validationQuery">${validationQuery}</prop> <prop key="testOnBorrow">false</prop> <prop key="testOnReturn">false</prop> <prop key="testWhileIdle">true</prop> <prop key="removeAbandoned">true</prop> <prop key="removeAbandonedTimeout">1800</prop> <prop key="logAbandoned">true</prop> <prop key="filters">mergeStat</prop> </props> </property> </bean> </beans>2:spring主配置文件spring-context.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" xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd"> <!-- 使用annotation 自动注册bean,并检查@Required,@Autowired的属性已被注入 --> <context:component-scan base-package="com.tx" /> <!-- 使用AspectJ方式配置AOP --> <aop:aspectj-autoproxy /> <!-- 引入属性配置文件 --> <bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> <property name="location" value="classpath:properties/database.properties" /> </bean> <!--或 <context:property-placeholder location="classpath*:*.properties" /> --> </beans>3:数据源配置参数database.properties:
#mysql-Used to verify the effectiveness of the database connection validationQuery=SELECT 1 jdbc.initialSize=5 jdbc.maxActive=20 jdbc.maxWait=60000 jdbc.poolPreparedStatements=false jdbc.poolMaximumIdleConnections=0 jdbc.driverClassName=org.gjt.mm.mysql.Driver jdbc.xaDataSourceClassName=com.alibaba.druid.pool.xa.DruidXADataSource #jdbc.xaDataSourceClassName=com.mysql.jdbc.jdbc2.optional.MysqlXADataSource #1.tms business. 2.The db level optimization,data concurrency,desirable. master.jdbc.url=jdbc:mysql://your ip:3306/master?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull slave.jdbc.url=jdbc:mysql://your ip:3306/slave?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull jdbc.username=username jdbc.password=password4:mybatis的配置mybatis-context.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" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd" default-lazy-init="true"> <description>MyBatis的数据库持久层配置/配置主-从数据源</description> <bean id="masterSqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="configLocation" value="classpath:mybatis/mybatis-config-master.xml" /> <property name="dataSource" ref="masterDataSource" /> </bean> <bean id="slaveSqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="configLocation" value="classpath:mybatis/mybatis-config-slave.xml" /> <property name="dataSource" ref="slaveDataSource" /> </bean> </beans>配置mybatis-config(此代码,只为测试分布式事务,并不涉及真实的业务!!!):
mybatis-config-master.xml:
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <typeAliases> <typeAlias alias="Member" type="com.tx.entity.Member"/> </typeAliases> <mappers> <mapper resource="com/tx/xml/MemberMapper.xml" /> </mappers> </configuration>mybatis-config-slave.xml
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <typeAliases> <typeAlias alias="MemberInfo" type="com.tx.entity.MemberInfo"/> </typeAliases> <mappers> <mapper resource="com/tx/xml/MemberInfoMapper.xml" /> </mappers> </configuration>5:Mapper的管理及注入,为mybatis的dao层mapper接口注入[绑定]sqlSessionFactory:
<?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:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd" default-lazy-init="true"> <description>MyBatis为不同的mapper注入sqlSessionFactory</description> <!-- Mapper的管理及注入 --> <bean id="memberMapper" class="org.mybatis.spring.mapper.MapperFactoryBean"> <property name="sqlSessionFactory" ref="masterSqlSessionFactory" /> <property name="mapperInterface" value="com.tx.dao.MemberMapper" /> </bean> <bean id="memberInfoMapper" class="org.mybatis.spring.mapper.MapperFactoryBean"> <property name="sqlSessionFactory" ref="slaveSqlSessionFactory" /> <property name="mapperInterface" value="com.tx.dao.MemberInfoMapper" /> </bean> </beans>6:atomikos事务配置transaction-context.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" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd" default-lazy-init="true"> <description>配置事物</description> <!-- atomikos事务管理器 --> <bean id="atomikosTransactionManager" class="com.atomikos.icatch.jta.UserTransactionManager" init-method="init" destroy-method="close"> <property name="forceShutdown"> <value>true</value> </property> </bean> <bean id="atomikosUserTransaction" class="com.atomikos.icatch.jta.UserTransactionImp"> <property name="transactionTimeout" value="300" /> </bean> <!-- spring 事务管理器 --> <bean id="springTransactionManager" class="org.springframework.transaction.jta.JtaTransactionManager"> <property name="transactionManager" ref="atomikosTransactionManager" /> <property name="userTransaction" ref="atomikosUserTransaction" /> <!-- 必须设置,否则程序出现异常 JtaTransactionManager does not support custom isolation levels by default --> <property name="allowCustomIsolationLevels" value="true"/> </bean> <aop:config proxy-target-class="true"> <aop:advisor pointcut="(execution(* com.tx.service.*.* (..)))" advice-ref="txAdvice" /> </aop:config> <tx:advice id="txAdvice" transaction-manager="springTransactionManager"> <tx:attributes> <tx:method name="get*" propagation="REQUIRED" read-only="true" /> <tx:method name="find*" propagation="REQUIRED" read-only="true" /> <tx:method name="has*" propagation="REQUIRED" read-only="true" /> <tx:method name="locate*" propagation="REQUIRED" read-only="true" /> <tx:method name="register*" propagation="REQUIRED" rollback-for="java.lang.Exception" /> </tx:attributes> </tx:advice> </beans>7:配置jta启动参数在src下,最后追加详细:
com.atomikos.icatch.service=com.atomikos.icatch.standalone.UserTransactionServiceFactory com.atomikos.icatch.console_file_name = /home/logs/tx/tx.out.log com.atomikos.icatch.log_base_name = txlog com.atomikos.icatch.tm_unique_name = com.atomikos.spring.jdbc.tm com.atomikos.icatch.console_log_level=DEBUG8:代码(部分不涉及代码已删除):
a:mybatis的mapper和dao接口[MemberMapper/MemberInfoMapper]:
package com.tx.dao; import com.tx.entity.Member; public interface MemberMapper { int insert(Member record); }
package com.tx.dao; import com.tx.entity.MemberInfo; public interface MemberInfoMapper { int insert(MemberInfo record); }
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" > <mapper namespace="com.tx.dao.MemberMapper" > <resultMap id="BaseResultMap" type="com.tx.entity.Member" > <id column="id" property="id" jdbcType="INTEGER" /> <result column="username" property="username" jdbcType="VARCHAR" /> <result column="password" property="password" jdbcType="VARCHAR" /> <result column="status" property="status" jdbcType="TINYINT" /> </resultMap> <sql id="Base_Column_List" > id, username, password, status </sql> <insert id="insert" parameterType="com.tx.entity.Member" > insert into member (id, username, password, status) values (#{id,jdbcType=INTEGER}, #{username,jdbcType=VARCHAR}, #{password,jdbcType=VARCHAR}, #{status,jdbcType=TINYINT}) </insert> </mapper>
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" > <mapper namespace="com.tx.dao.MemberInfoMapper" > <resultMap id="BaseResultMap" type="com.tx.entity.MemberInfo" > <id column="id" property="id" jdbcType="INTEGER" /> <result column="nickname" property="nickname" jdbcType="VARCHAR" /> <result column="realname" property="realname" jdbcType="VARCHAR" /> <result column="age" property="age" jdbcType="TINYINT" /> </resultMap> <sql id="Base_Column_List" > id, nickname, realname, age </sql> <insert id="insert" parameterType="com.tx.entity.MemberInfo" > insert into member_info (id, nickname, realname, age) values (#{id,jdbcType=INTEGER}, #{nickname,jdbcType=VARCHAR}, #{realname,jdbcType=VARCHAR}, #{age,jdbcType=TINYINT}) </insert> </mapper>b:服务层接口和实现:
package com.tx.sevice; import com.tx.entity.Member; import com.tx.entity.MemberInfo; public interface MemberService { boolean registerMember(Member member, MemberInfo memberInfo); }
package com.tx.sevice.impl; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import com.tx.dao.MemberInfoMapper; import com.tx.dao.MemberMapper; import com.tx.entity.Member; import com.tx.entity.MemberInfo; import com.tx.sevice.MemberService; @Service("memberService") public class MemberServiceImpl implements MemberService { //log private static final Logger LOG = LoggerFactory.getLogger(MemberServiceImpl.class); @Autowired private MemberMapper memberMapper; @Autowired private MemberInfoMapper memberInfoMapper; @Override public boolean registerMember(Member member, MemberInfo memberInfo) { boolean resRegister = false; try { if(memberMapper.insert(member) != 1){ throw new RuntimeException("注册用户:Member表数据插入不一致."); } if(memberInfoMapper.insert(memberInfo) != 1){ throw new RuntimeException("注册用户:MemberInfo表数据插入不一致."); } resRegister = true; } catch (Exception e) { LOG.info("注册用户:数据库保存异常." + e.getMessage(), e); throw new RuntimeException("注册用户:数据库保存异常"); } return resRegister; } }c:junit测试代码:
package com.tx.test; import org.junit.Test; import org.junit.runner.RunWith; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import com.tx.entity.Member; import com.tx.entity.MemberInfo; import com.tx.sevice.MemberService; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = {"classpath:spring-context.xml","classpath:datasource-context.xml", "classpath:mybatis-context.xml","classpath:mapper-context.xml","classpath:transaction-context.xml"}) public class JTATest { //log private static final Logger LOG = LoggerFactory.getLogger(JTATest.class); @Autowired private MemberService memberService; @Test public void testRegister(){ Member member = new Member(); member.setId(2); member.setUsername("童可可"); member.setPassword("12345678"); member.setStatus((byte)0); MemberInfo memberInfo = new MemberInfo(); memberInfo.setId(2); memberInfo.setAge((byte)25); memberInfo.setNickname("keke"); memberInfo.setRealname("童可可"); if(memberService.registerMember(member, memberInfo)){ LOG.info("##用户注册成功"); }else{ LOG.info("##用户注册失败"); } } }注:通过主键重复可以测试回滚,数据没问题,正常提交不同数据库!log4j和web.xml和sql,其他代码: Spring实现数据库读写分离/spring事务配置解释
效果图:
执行前,master数据库:
执行前,slave数据库:
执行后,master数据库:
执行后,slave数据库:
spring-tx-2.5.6.jar和spring-tx-3.0.0.RELEASE.jar目录:
jta.properties启动参数:
# SAMPLE PROPERTIES FILE FOR THE TRANSACTION SERVICE # THIS FILE ILLUSTRATES THE DIFFERENT SETTINGS FOR THE TRANSACTION MANAGER # UNCOMMENT THE ASSIGNMENTS TO OVERRIDE DEFAULT VALUES; # Required: factory implementation class of the transaction core. # NOTE: there is no default for this, so it MUST be specified! # com.atomikos.icatch.service=com.atomikos.icatch.standalone.UserTransactionServiceFactory # Set base name of file where messages are output # (also known as the 'console file'). # com.atomikos.icatch.console_file_name = tm.out # Size limit (in bytes) for the console file; # negative means unlimited. # # com.atomikos.icatch.console_file_limit=-1 # For size-limited console files, this option # specifies a number of rotating files to # maintain. # # com.atomikos.icatch.console_file_count=1 # Set the number of log writes between checkpoints # # com.atomikos.icatch.checkpoint_interval=500 # Set output directory where console file and other files are to be put # make sure this directory exists! # # com.atomikos.icatch.output_dir = ./ # Set directory of log files; make sure this directory exists! # # com.atomikos.icatch.log_base_dir = ./ # Set base name of log file # this name will be used as the first part of # the system-generated log file name # com.atomikos.icatch.log_base_name = tmlog # Set the max number of active local transactions # or -1 for unlimited. # # com.atomikos.icatch.max_actives = 50 # Set the default timeout (in milliseconds) for local transactions # # com.atomikos.icatch.default_jta_timeout = 10000 # Set the max timeout (in milliseconds) for local transactions # # com.atomikos.icatch.max_timeout = 300000 # The globally unique name of this transaction manager process # override this value with a globally unique name # com.atomikos.icatch.tm_unique_name = tm # Do we want to use parallel subtransactions? JTA's default # is NO for J2EE compatibility # # com.atomikos.icatch.serial_jta_transactions=true # If you want to do explicit resource registration then # you need to set this value to false. # # com.atomikos.icatch.automatic_resource_registration=true # Set this to WARN, INFO or DEBUG to control the granularity # of output to the console file. # com.atomikos.icatch.console_log_level=INFO # Do you want transaction logging to be enabled or not? # If set to false, then no logging overhead will be done # at the risk of losing data after restart or crash. # # com.atomikos.icatch.enable_logging=true # Should two-phase commit be done in (multi-)threaded mode or not? # Set this to false if you want commits to be ordered according # to the order in which resources are added to the transaction. # # NOTE: threads are reused on JDK 1.5 or higher. # For JDK 1.4, thread reuse is enabled as soon as the # concurrent backport is in the classpath - see # http://mirrors.ibiblio.org/pub/mirrors/maven2/backport-util-concurrent/backport-util-concurrent/ # # com.atomikos.icatch.threaded_2pc=false # Should shutdown of the VM trigger shutdown of the transaction core too? # # com.atomikos.icatch.force_shutdown_on_vm_exit=false
分布式事务操作之Spring+JTA可参照: http://www.cnblogs.com/wangyong/p/4174326.html
Atomikos 中文说明文档:http://blog.csdn.net/sun8288/article/details/8674016