记录学习的点滴(Spring+Mybatis+Atomikos+Tomcat实现分布式事务管理)

当更新的多个表不在同一数据库的时候,但是它们又必须在同一事务内,这个时候分布式事务就派上用场啦。

用到的jar包

atomikos-util.3.7.0.jar
transactions-3.7.0.jar
transactions-api-3.7.0.jar
transactions-jdbc-3.7.0.jar
transactions-jta-3.7.0.jar
jta-1.1.jar

配置很关键。

<?xml version="1.0" encoding="UTF-8"?>
<beans  
    xmlns="http://www.springframework.org/schema/beans"  
    xmlns:tx="http://www.springframework.org/schema/tx"  
    xmlns:p="http://www.springframework.org/schema/p"  
    xmlns:aop="http://www.springframework.org/schema/aop"   
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xmlns:mvc="http://www.springframework.org/schema/mvc" 
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans   
    http://www.springframework.org/schema/beans/spring-beans-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/aop    
    http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context-3.0.xsd    
    http://www.springframework.org/schema/mvc
    http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">
    <!-- 引入数据源信息的properties属性文件 -->
    <context:property-placeholder location="classpath:db.properties" />
	<!-- XA方式 -->
	<!-- MYSQL数据库配置 -->
	<bean id="mysqlDataSource" class="com.atomikos.jdbc.AtomikosDataSourceBean" destroy-method="close">
		<property name="uniqueResourceName" value="dataSource1"/>
	    <property name="xaDataSourceClassName" value="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource"/>
		<property name="xaProperties">
			<props>
				<prop key="URL">${mysql.url}</prop>
				<prop key="user">${mysql.username}</prop>
				<prop key="password">${mysql.password}</prop>
			</props>
		</property>
	    <property name="minPoolSize" value="10" />
	    <property name="maxPoolSize" value="100" />
	    <property name="borrowConnectionTimeout" value="30" />
	    <property name="maintenanceInterval" value="60" />
	</bean>
	
	<!-- ORACLE数据库配置 -->
	<bean id="oracleDataSource" class="com.atomikos.jdbc.AtomikosDataSourceBean" destroy-method="close">
		<property name="uniqueResourceName" value="dataSource2"/>
		<property name="xaDataSourceClassName" value="oracle.jdbc.xa.client.OracleXADataSource" />
		<property name="xaProperties">
			<props>
				<prop key="URL">${orcale.url}</prop>
				<prop key="user">${orcale.username}</prop>
				<prop key="password">${orcale.password}</prop>
			</props>
		</property>
		<property name="minPoolSize" value="10" />
		<property name="maxPoolSize" value="100" />
		<property name="borrowConnectionTimeout" value="30" />
		<property name="maintenanceInterval" value="60" />
	</bean>

 	<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
 		<property name="configLocation" value="classpath:mybatis-config-mysql.xml" />
	    <property name="dataSource" ref="mysqlDataSource" />
	</bean>
 	<bean id="sqlSessionFactoryOracle" class="org.mybatis.spring.SqlSessionFactoryBean">
 		<property name="configLocation" value="classpath:mybatis-config.xml" />
	    <property name="dataSource" ref="oracleDataSource" />
	</bean>
	
    <!-- MyBatis为不同的mapper注入sqlSessionFactory -->
    <bean id="mysqlTransactionTestDao" class="org.mybatis.spring.mapper.MapperFactoryBean">
        <property name="sqlSessionFactory" ref="sqlSessionFactory" />
        <property name="mapperInterface" value="com.springmybatis.system.dao.mysqltransaction.MysqlTransactionTestDao" />
    </bean>
    <bean id="transactionTestDao" class="org.mybatis.spring.mapper.MapperFactoryBean">
        <property name="sqlSessionFactory" ref="sqlSessionFactoryOracle" />
        <property name="mapperInterface" value="com.springmybatis.system.dao.transaction.TransactionTestDao" />
    </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 transaction-manager="transactionManager"/>
    
	<!-- 开启注解 -->
	<context:annotation-config />
	<!-- base-package指向注解要扫描的包 -->
	<context:component-scan base-package="com.springmybatis.system" />

	<!-- 注册拦截器 -->
	<mvc:interceptors>
		<bean class="com.springmybatis.system.interceptor.MyInterceptor" />
	</mvc:interceptors>
</beans>

JUnit测试类。

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:applicationContext.xml"})
public class TransactionTestMain {
	@Autowired
	private TransactionTestService transactionTestService;
	
	/**
	 * 在同一事务有多个数据源
	 */
	@Test
	public void multipleDataSource2() {
		transactionTestService.updateMultipleDataSource(1, 2, 200L);
	}
}

Service接口。

public interface TransactionTestService {
	
	/**
	 * 转账操作
	 * @param deUserId  转出账号
	 * @param inUserid  装入账号
	 * @param money     交易金额
	 */
	public void updateMultipleDataSource(int deUserId, int inUserid, long money);	
}

Service实现类。

@Service
public class TransactionTestServiceImpl implements TransactionTestService {
	@Autowired
	@Qualifier("mysqlTransactionTestDao")
	private MysqlTransactionTestDao mysqlTransactionTestDao;
	
	@Autowired
	@Qualifier("transactionTestDao")
	private TransactionTestDao transactionTestDao;
	
	/**
	 * 在同一事务有多个数据源
	 */
	@Override
	@Transactional
	public void updateMultipleDataSource(int deUserId, int inUserid, long money) {
		// 账户1转出操作
		mysqlTransactionTestDao.decreaseMoney(deUserId, money);
		
		// 账户2转入操作
		transactionTestDao.increaseMoney(inUserid, money);
		
	}	

}

MysqlTransactionTestDao接口。

public interface MysqlTransactionTestDao {
	
	public void decreaseMoney(Integer userId, Long money);

}


MysqlTransactionTestDao实现类。

@Repository
public class MysqlTransactionTestDaoImpl extends SqlSessionDaoSupport implements MysqlTransactionTestDao {
	// 1,重写父类 【SqlSessionDaoSupport】方法实现注入【SqlSessionFactory】。
	// 2,必须要有,在配置文件【applicationContext.xml】要配置过。
	@Resource	
	public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
		super.setSqlSessionFactory(sqlSessionFactory);
	}

	/**
	 * 转出操作
	 * @param userId    转出账号
	 * @param moneyNm   交易金额
	 */
	@Override
	public void decreaseMoney(@Param("userId") Integer userId, @Param("moneyNm") Long moneyNm) {
		// 参数设定
		TransactionTestDao mapper = super.getSqlSession().getMapper(TransactionTestDao.class);
		mapper.decreaseMoney(userId, moneyNm);
	}
}

MysqlTransactionTestDao.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="com.springmybatis.system.dao.mysqltransaction.MysqlTransactionTestDao">
<!-- 开启二级缓存 -->
<!-- eviction     :回收策略 -->
<!-- flushInterval:自动刷新时间 -->
<!-- size         :最多缓存对象数 -->
<!-- readOnly     :只读 -->
<cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true" />
	<update id="decreaseMoney" parameterType="java.util.Map">
		UPDATE if_kikaku.test_accout SET balance=balance - #{1,jdbcType=BIGINT} WHERE user_id=#{0,jdbcType=INTEGER}
	</update>
</mapper>

TransactionTestDao接口。

public interface TransactionTestDao {
	
	public void increaseMoney(Integer userId, Long moneyNm);
}

TransactionTestDao实现类。

@Repository
public class TransactionTestDaoImpl extends SqlSessionDaoSupport implements TransactionTestDao {

	// 1,重写父类 【SqlSessionDaoSupport】方法实现注入【sqlSessionFactory】。
	// 2,必须要有,在配置文件【applicationContext.xml】要配置过。
	@Resource	
	public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory){
		super.setSqlSessionFactory(sqlSessionFactory);
	}
	
	/**
	 * 转入操作
	 * @param userId    装入账号
	 * @param money     交易金额
	 */	
	@Override
	public void increaseMoney(@Param("userId") Integer userId, @Param("moneyNm") Long moneyNm) {
		TransactionTestDao mapper = super.getSqlSession().getMapper(TransactionTestDao.class);
		mapper.increaseMoney(userId, moneyNm);
	}
}

TransactionTestDao.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="com.springmybatis.system.dao.transaction.TransactionTestDao">
<!-- 开启二级缓存 -->
<!-- eviction     :回收策略 -->
<!-- flushInterval:自动刷新时间 -->
<!-- size         :最多缓存对象数 -->
<!-- readOnly     :只读 -->
<cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true" />
	<delete id="increaseMoney">
		UPDATE if_kikaku.test_accout SET balance=balance + #{1,jdbcType=BIGINT} WHERE user_id=#{0,jdbcType=INTEGER}
	</delete>
</mapper>

atomikos的配置文件jta.properties
com.atomikos.icatch.service=com.atomikos.icatch.standalone.UserTransactionServiceFactory
com.atomikos.icatch.console_file_name=tm.out
com.atomikos.icatch.log_base_name=jdbc
com.atomikos.icatch.tm_unique_name=com.atomikos.spring.jdbc.tm
com.atomikos.icatch.console_log_level=false

代码结构如下图。

mybatis-config-mysql.xml扫描的mapper文件。

	<mappers>
 		<package name="com.springmybatis.system.dao.mysqltransaction" />
	</mappers>

mybatis-config.xml扫描的mapper文件。

	<mappers>
		<!-- 通过mapper元素的resource属性可以指定相对于类路径的Mapper.xml文件 -->
 		<mapper resource="com/springmybatis/system/dao/TestUserDao.xml" />
 		<!-- 通过mapper元素的class属性可以指定Mapper接口进行注册 -->
 		<mapper class="com.springmybatis.system.dao.UserDao" />
 		<!-- 通过package元素将会把指定包下面的所有Mapper接口进行注册 -->
 		<package name="com.springmybatis.system.dao.transaction" />
	</mappers>










  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值