一、概述:
本文主要讲述如何基于Atomikos 和spring在项目中实现分布式事务管理
二、应用场景:
如果项目中的数据源来自多个数据库,同时又需要在多数据源中保证事务,此时就需要用到分布式事务处理了。
三、实验模拟需求:
比如有两个对象:用户信息、用户存款,用户信息存在数据库A、存款信息存在数据库B,若客户甲向乙转账,需要在数据库B中对甲、乙的存款信息修改,同时在数据库A中把甲、乙的备注信息最新为最近一次的操作时间。
四、实例测试环境:
• spring、hibernate3.2
• mysql5.1.51(需要版本5.0+)
• AtomikosTransactionsEssentials-3.7.0 (详细可参加它的官网:http://www.atomikos.com )
说明:
1. 测试的数据库需要支持分布式事务,同时JDBC要支持XA连接驱动。本次测试用的mysql5.1是支持事务的,JDBC驱动版本:mysql-connector-java-5.1.7-bin.jar,包含对 XA连接的支持:com.mysql.jdbc.jdbc2.optional.MysqlXAConnection。
五、代码及配置介绍:
源代码下载:分布式事务实例演示源代码michael_jta_code.zip
1.代码的目录结构图如下:
转账代码片段:
/** * 转账测试 * @param srcId * @param destId * @param money * @return boolean */ public boolean doTestTransfer(String srcId, String destId, float money) { BankAccount srcAccount = bankAccountDao.getByUserName(srcId); BankAccount destAccount = bankAccountDao.getByUserName(destId); if (srcAccount.getDeposit() < money) { System.out.println("warn :" + srcAccount.getUserName() + " has not enough money to transfer"); return false; } srcAccount.setDeposit(srcAccount.getDeposit() - money); destAccount.setDeposit(destAccount.getDeposit() + money); bankAccountDao.update(srcAccount); bankAccountDao.update(destAccount); Date curTime = new Date(); UserInfo srcUser = userInfoDao.getById(srcId); UserInfo destUser = userInfoDao.getById(destId); destUser.setRemark1(curTime + ""); destUser.setRemark2(curTime + ""); userInfoDao.update(destUser); srcUser.setRemark1(curTime + ""); if (srcAccount.getDeposit() < 18000) { throw new RuntimeException("michael test exception for JTA "); } srcUser.setRemark2(curTime + ""); userInfoDao.update(srcUser); System.out.println("success done:" + srcAccount.getUserName() + " transfer ¥" + money + " to " + destAccount.getUserName()); return true; }
jta.jdbc.properties:
jdbc.SDS.class=com.mysql.jdbc.jdbc2.optional.MysqlXADataSource jdbc.SDS.properties=URL=jdbc:mysql://localhost:3306/Test1;user=root;password=root jdbc.SDS2.class=com.mysql.jdbc.jdbc2.optional.MysqlXADataSource jdbc.SDS2.properties=URL=jdbc:mysql://localhost:3306/Test2;user=root;password=root
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 = tmlog com.atomikos.icatch.tm_unique_name = com.atomikos.spring.jdbc.tm com.atomikos.icatch.console_log_level = INFO
六、测试验证
1. 初始化数据:
因为mysql数据库表的类型有事务和非事务之分,建表时一定要注意确保表的类型是事务控制的:InnoDB
以下两张表请分部在两个不同的数据库
CREATE TABLE tb_user_info ( user_name varchar(20), real_name varchar(10), remark1 varchar(50), remark2 varchar(50) ) ENGINE = InnoDB; INSERT INTO tb_user_info (user_name,real_name,remark1,remark2) VALUES ('husband','husband','',''); INSERT INTO tb_user_info (user_name,real_name,remark1,remark2) VALUES ('wife','wife','','');
CREATE TABLE tb_account ( id int AUTO_INCREMENT, user_name varchar(20), deposit float(10,2), PRIMARY KEY(id) ) ENGINE = InnoDB; INSERT INTO tb_account (user_name,deposit) VALUES ('husband',20000.00); INSERT INTO tb_account (user_name,deposit) VALUES ('wife',10000.00);