最近刚刚转行java开发。入职第一天,要实现将数据保存到mysql和sql server数据库中,并保证事务性,各种查阅资料,以下是完成成果,记录一下:
首先是jar包引入:
关于Atomikos相关jar包:
transactions-jdbc
transactions-jta
transactions-api
transactions
atomikos-utils
关于jta包:
jta-1.1
jdbc.properties:
#=================================================
# MySQL
#=================================================
jdbc.mysql.driver=com.mysql.jdbc.Driver
jdbc.mysql.url=jdbc:mysql://127.0.0.1:3306/crm?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true
jdbc.mysql.username=root
jdbc.mysql.password=root
#=================================================
# MS SQL Server
#=================================================
jdbc.sqlserver.username=sa
jdbc.sqlserver.password=pwd@123
#=================================================
# 通用配置
#=================================================
jdbc.initialSize=5
jdbc.minIdle=5
jdbc.maxIdle=20
jdbc.maxActive=100
jdbc.maxWait=100000
jdbc.defaultAutoCommit=false
jdbc.removeAbandoned=true
jdbc.removeAbandonedTimeout=600
jdbc.testWhileIdle=true
jdbc.timeBetweenEvictionRunsMillis=60000
jdbc.numTestsPerEvictionRun=20
jdbc.minEvictableIdleTimeMillis=300000
log4j.properties
#\u5B9A\u4E49\u8F93\u51FA\u683C\u5F0F
ConversionPattern=%d %-5p [%t] %c - %m%n
log4j.rootLogger=DEBUG,Console
log4j.logger.com.cnblogs.lzrabbit=DEBUG
log4j.logger.org.springframework=ERROR
log4j.logger.org.mybatis=ERROR
log4j.logger.org.apache.ibatis=ERROR
log4j.logger.org.quartz=ERROR
log4j.logger.org.apache.axis2=ERROR
log4j.logger.org.apache.axiom=ERROR
log4j.logger.org.apache=ERROR
log4j.logger.httpclient=ERROR
#log4j.additivity.org.springframework=false
#Console
log4j.appender.Console=org.apache.log4j.ConsoleAppender
log4j.appender.Console.Threshold=DEBUG
log4j.appender.Console.Target=System.out
log4j.appender.Console.layout=org.apache.log4j.PatternLayout
log4j.appender.Console.layout.ConversionPattern=${ConversionPattern}
#log4j.appender.Console.encoding=UTF-8
#org.apache.log4j.DailyRollingFileAppender
log4j.appender.DailyFile=org.apache.log4j.DailyRollingFileAppender
log4j.appender.DailyFile.DatePattern='.'yyyy-MM-dd'.log'
log4j.appender.DailyFile.File=${myApp.root}/logs/daily.log
log4j.appender.DailyFile.Append=true
log4j.appender.DailyFile.Threshold=DEBUG
log4j.appender.DailyFile.layout=org.apache.log4j.PatternLayout
log4j.appender.DailyFile.layout.ConversionPattern=${ConversionPattern}
log4j.appender.DailyFile.encoding=UTF-8
jta.properties
com.atomikos.icatch.service=com.atomikos.icatch.standalone.UserTransactionServiceFactory
com.atomikos.icatch.log_base_name = jdbc
com.atomikos.icatch.tm_unique_name = com.atomikos.spring.jdbc.tm
com.atomikos.icatch.serializable_logging=false
mybatis-config.xml
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!-- setting -->
<!-- 别名 -->
<typeAliases>
<package name="cn.test.core.bean"/>
</typeAliases>
<!-- Mapper位置 注意: Dao接口与Mapper配置文件在一个目录下,并且 同名 可以不做如下配置 -->
<!--
<mappers>
<package name="cn.test.core.dao"/>
</mappers>
-->
</configuration>
application-context.xml:
<!-- 导入全部的配置文件此处方便查看在一个配置文件中 -->
<!-- <import resource="config/*.xml"/> -->
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:jdbc.properties</value>
</list>
</property>
</bean>
<!-- One -->
<!-- XA方式 -->
<bean id="oneDataSource" class="com.atomikos.jdbc.AtomikosDataSourceBean" init-method="init" destroy-method="close">
<property name="uniqueResourceName" value="db_one1"/>
<property name="xaDataSourceClassName" value="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource"/>
<property name="xaProperties">
<props>
<prop key="url">${jdbc.mysql.url}</prop>
<prop key="user">${jdbc.mysql.username}</prop>
<prop key="password">${jdbc.mysql.password}</prop>
</props>
</property>
<property name="minPoolSize" value="10" />
<property name="maxPoolSize" value="100" />
<property name="borrowConnectionTimeout" value="30" />
<property name="testQuery" value="select 1" />
<property name="maintenanceInterval" value="60" />
</bean>
<bean id="oneSqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="oneDataSource"/>
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<property name="mapperLocations" value="classpath*:dao/one/*.xml"/>
</bean>
<!-- Two -->
<!-- XA方式 -->
<bean id="twoDataSource" class="com.atomikos.jdbc.AtomikosDataSourceBean" init-method="init" destroy-method="close">
<property name="uniqueResourceName" value="db_two2"/>
<property name="xaDataSourceClassName" value="com.microsoft.sqlserver.jdbc.SQLServerXADataSource"/>
<property name="xaProperties">
<props>
<prop key="serverName">192.168.1.122</prop>
<prop key="databaseName">change</prop>
<prop key="user">${jdbc.sqlserver.username}</prop>
<prop key="password">${jdbc.sqlserver.password}</prop>
</props>
</property>
<property name="minPoolSize" value="10" />
<property name="maxPoolSize" value="100" />
<property name="borrowConnectionTimeout" value="30" />
<property name="testQuery" value="select 1" />
<property name="maintenanceInterval" value="60" />
</bean>
<bean id="twoSqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="twoDataSource"/>
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<property name="mapperLocations" value="classpath*:dao/two/*.xml"/>
</bean>
<!-- 分布式事务 -->
<bean id="atomikosTransactionManager" class="com.atomikos.icatch.jta.UserTransactionManager" init-method="init" destroy-method="close">
<property name="forceShutdown" value="true"/>
</bean>
<bean id="atomikosUserTransaction" class="com.atomikos.icatch.jta.UserTransactionImp">
<property name="transactionTimeout" value="300"/>
</bean>
<bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager">
<property name="transactionManager" ref="atomikosTransactionManager"/>
<property name="userTransaction" ref="atomikosUserTransaction"/>
</bean>
<tx:annotation-driven/>
<!-- Service -->
<bean id="testJtaService" class="cn.test.core.service.TestJtaServiceImpl">
<property name="testTbDao" ref="testTbDao"></property>
<property name="testUserDao" ref="testUserDao"></property>
</bean>
<!-- Dao -->
<bean id="testTbDao" class="org.mybatis.spring.mapper.MapperFactoryBean">
<property name="mapperInterface" value="cn.test.core.dao.one.TestTbDao"></property>
<property name="sqlSessionFactory" ref="oneSqlSessionFactory"></property>
</bean>
<bean id="testUserDao" class="org.mybatis.spring.mapper.MapperFactoryBean">
<property name="mapperInterface" value="cn.test.core.dao.two.TestUserDao"></property>
<property name="sqlSessionFactory" ref="twoSqlSessionFactory"></property>
</bean>
</beans>
下面就是测试分布式事务代码:截图说明目录结构和相关文件:(忽略config目录)
测试数据的数据表:
测试bean ,就很简单了,不做展示,提供带参数构造方法,方便测试。
按照目录结构,逐一展示其代码:
TestTbDao.java:
package cn.test.core.dao.one;
import org.springframework.stereotype.Repository;
import cn.test.core.bean.TestTb;
/**
* 测试 Dao 接口
* @author Barry
*/
public interface TestTbDao {
//测试添加
void addTestTb(TestTb testTb);
}
TestTbDao.xml:
<?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="cn.test.core.dao.one.TestTbDao">
<!-- 添加测试数据 -->
<insert id="addTestTb" parameterType="TestTb">
insert into tb_test
(id,name)
values
(#{id},#{name})
</insert>
</mapper>
TestUserDao.java:
package cn.test.core.dao.two;
import org.springframework.stereotype.Repository;
import cn.test.core.bean.TestUser;
public interface TestUserDao {
//测试添加
void addTestUser(TestUser testUser);
}
TestUserDao.xml:
<?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="cn.test.core.dao.two.TestUserDao">
<!-- 添加测试数据 -->
<insert id="addTestUser" parameterType="cn.test.core.bean.TestUser">
insert into tb_user
(t_id,t_name)
values
(#{t_id},#{t_name})
</insert>
</mapper>
TestJtaService.java :
public interface TestJtaService {
void add(TestTb testTb, TestUser testUser) ;
}
TestJtaServiceImpl.java:
@Service
@Transactional
public class TestJtaServiceImpl implements TestJtaService {
private TestTbDao testTbDao;
private TestUserDao testUserDao;
public void setTestTbDao(TestTbDao testTbDao) {
this.testTbDao = testTbDao;
}
public void setTestUserDao(TestUserDao testUserDao) {
this.testUserDao = testUserDao;
}
@Override
public void add(TestTb testTb, TestUser testUser) {
testTbDao.addTestTb(testTb);
//测试分布式事务
//int i = 1/0;
testUserDao.addTestUser(testUser);
}
}
public class TestTestTb {
ApplicationContext context = null;
TestJtaService testJtaService = null;
@Before
public void initContext(){
this.context = new FileSystemXmlApplicationContext("classpath:application-context.xml");
this.testJtaService = (TestJtaService) context.getBean("testJtaService");
}
@Test
public void testAdd() {
TestTb testTb = new TestTb(33,"one");
TestUser testUser = new TestUser(33,"two");
testJtaService.add(testTb, testUser);
}
}
至此所有配置和测试代码都已完成,测试去掉服务层实现类的注释即可看到效果。
参考相关:
http://loftor.com/archives/spring-mybatis-tomcat-jta.html
http://zxlaiye.iteye.com/blog/1441299
最后在提供一个maven 仓库:
https://repository.sonatype.org/index.html