分布式事务JTA之实践:Spring+ATOMIKOS
来源:互联网 发布:火柴盒软件 编辑:程序博客网 时间:2024/04/26 07:42
http://sjsky.iteye.com/blog/1112012
http://www.micmiu.com/category/j2ee/jta/
在2011.7.1号这个特殊的日子,发一blog,名为:分布式事务JTA之实践:Spring+ATOMIKOS 来庆祝党的90岁生日和iteye博客产品成功改版上线。
本文的目录结构如下:
- 一、概述
- 二、应用场景
- 三、实验模拟需求
- 四、实例测试环境
- 五、源代码下载及配置介绍
- 六、测试验证
一、概述:
本文主要讲述如何基于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。
2. 附件提供AtomikosTransactionsEssentials 3.7.0 lib包下载:AtomikosTransactionsEssentials-3.7.0-lib.zip
官方下载地址:http://www.atomikos.com/Main/TransactionsEssentialsDownloadForm ,需要先注册才能下载。同时这里也提供目前3.7.0的下载链接:http://www.atomikos.com/downloads/transactions-essentials/com/atomikos/AtomikosTransactionsEssentials/3.7.0/AtomikosTransactionsEssentials-3.7.0-bin.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;
- }
2. 配置文件详细介绍:
jta.jdbc.properties
- #see http://sjsky.iteye.com
- # eg. for mysql
- jdbc.SDS.class=com.mysql.jdbc.jdbc2.optional.MysqlXADataSource
- jdbc.SDS.properties=URL=jdbc:mysql://192.168.8.253:3306/demota;user=root;password=111111
- jdbc.SDS2.class=com.mysql.jdbc.jdbc2.optional.MysqlXADataSource
- jdbc.SDS2.properties=URL=jdbc:mysql://192.168.8.150:3306/demota;user=root;password=111111
jta.properties
- #see http://sjsky.iteye.com
- 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
jta1.hibernate.cfg.xml
- <!DOCTYPE hibernate-configuration
- PUBLIC "-//Hibernate/Hibernate Configuration DTD//EN"
- "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
- <hibernate-configuration>
- <session-factory>
- <property name="dialect">
- org.hibernate.dialect.MySQL5Dialect
- </property>
- <property name="hbm2ddl.auto">update</property>
- <mapping class="michael.jta.atomikos.domain.UserInfo" />
- </session-factory>
- </hibernate-configuration>
jta2.hibernate.cfg.xml
- <!DOCTYPE hibernate-configuration
- PUBLIC "-//Hibernate/Hibernate Configuration DTD//EN"
- "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
- <hibernate-configuration>
- <session-factory>
- <property name="show_sql">true</property>
- <property name="dialect">
- org.hibernate.dialect.MySQL5Dialect
- </property>
- <property name="hbm2ddl.auto">update</property>
- <mapping class="michael.jta.atomikos.domain.BankAccount" />
- </session-factory>
- </hibernate-configuration>
jta.spring.xml
- <?xml version="1.0" encoding="UTF-8"?>
- <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
- <beans>
- <!-- Configurer that replaces ${...} placeholders with values from properties files -->
- <bean id="propertyConfigurer"
- class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
- <property name="locations">
- <list>
- <value>classpath:jta.jdbc.properties</value>
- </list>
- </property>
- </bean>
- <!-- 数据源配置 http://sjsky.iteye.com-->
- <bean id="SDS" class="com.atomikos.jdbc.SimpleDataSourceBean"
- init-method="init" destroy-method="close">
- <property name="uniqueResourceName">
- <value>SDS</value>
- </property>
- <property name="xaDataSourceClassName">
- <value>${jdbc.SDS.class}</value>
- </property>
- <property name="xaDataSourceProperties">
- <value>${jdbc.SDS.properties}</value>
- </property>
- <property name="exclusiveConnectionMode">
- <value>true</value>
- </property>
- <property name="connectionPoolSize">
- <value>3</value>
- </property>
- <property name="validatingQuery">
- <value>SELECT 1</value>
- </property>
- </bean>
- <bean id="SDS2" class="com.atomikos.jdbc.SimpleDataSourceBean"
- init-method="init" destroy-method="close">
- <property name="uniqueResourceName">
- <value>SDS2</value>
- </property>
- <property name="xaDataSourceClassName">
- <value>${jdbc.SDS2.class}</value>
- </property>
- <property name="xaDataSourceProperties">
- <value>${jdbc.SDS2.properties}</value>
- </property>
- <property name="exclusiveConnectionMode">
- <value>true</value>
- </property>
- <property name="connectionPoolSize">
- <value>3</value>
- </property>
- <property name="validatingQuery">
- <value>SELECT 1</value>
- </property>
- </bean>
- <!-- sessionFactory http://sjsky.iteye.com-->
- <bean id="sessionFactory1"
- class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
- <property name="dataSource" ref="SDS" />
- <property name="configLocation"
- value="classpath:jta1.hibernate.cfg.xml" />
- </bean>
- <bean id="sessionFactory2"
- class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
- <property name="dataSource" ref="SDS2" />
- <property name="configLocation"
- value="classpath:jta2.hibernate.cfg.xml" />
- </bean>
- <!-- TransactionManager http://sjsky.iteye.com-->
- <!-- Construct Atomikos UserTransactionManager, needed to configure Spring -->
- <bean id="atomikosTransactionManager"
- class="com.atomikos.icatch.jta.UserTransactionManager"
- init-method="init" destroy-method="close">
- <!-- when close is called, should we force transactions to terminate or not? -->
- <property name="forceShutdown">
- <value>true</value>
- </property>
- </bean>
- <!-- Also use Atomikos UserTransactionImp, needed to configure Spring -->
- <bean id="atomikosUserTransaction"
- class="com.atomikos.icatch.jta.UserTransactionImp">
- <property name="transactionTimeout">
- <value>300</value>
- </property>
- </bean>
- <!-- Configure the Spring framework to use JTA transactions from Atomikos -->
- <bean id="springJTATransactionManager"
- class="org.springframework.transaction.jta.JtaTransactionManager">
- <property name="transactionManager">
- <ref bean="atomikosTransactionManager" />
- </property>
- <property name="userTransaction">
- <ref bean="atomikosUserTransaction" />
- </property>
- </bean>
- <!-- Configure DAO http://sjsky.iteye.com-->
- <bean id="userInfoDao"
- class="michael.jta.atomikos.dao.impl.UserInfoDaoImpl">
- <property name="sessionFactory" ref="sessionFactory1" />
- </bean>
- <bean id="bankAccountDao"
- class="michael.jta.atomikos.dao.BankAccountDao">
- <property name="sessionFactory" ref="sessionFactory2" />
- </bean>
- <bean id="bankAccountService"
- class="michael.jta.atomikos.service.impl.BankAccountServiceImpl">
- <property name="userInfoDao" ref="userInfoDao" />
- <property name="bankAccountDao" ref="bankAccountDao" />
- </bean>
- <!-- 定义事务规则的拦截器 http://sjsky.iteye.com-->
- <bean id="transactionInterceptor"
- class="org.springframework.transaction.interceptor.TransactionInterceptor">
- <property name="transactionManager"
- ref="springJTATransactionManager" />
- <property name="transactionAttributes">
- <props>
- <prop key="*">PROPAGATION_REQUIRED</prop>
- </props>
- </property>
- </bean>
- <!-- 声明式事务边界配置 所有的bean公用一个代理bean http://sjsky.iteye.com-->
- <bean id="baseTransactionProxy"
- class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"
- abstract="true">
- <property name="transactionManager"
- ref="springJTATransactionManager" />
- <property name="transactionAttributes">
- <props>
- <!-- 可以根据实际情况细化配置提高性能 -->
- <prop key="*">PROPAGATION_REQUIRED</prop>
- </props>
- </property>
- </bean>
- <bean id="bankAccountServiceProxy" parent="baseTransactionProxy">
- <property name="target">
- <ref bean="bankAccountService" />
- </property>
- </bean>
- </beans>
六、测试验证
1. 初始化数据:
因为mysql数据库表的类型有事务和非事务之分,建表时一定要注意确保表的类型是事务控制的:InnoDB
数据库A(192.168.8.253):
- DROP DATABASE IF EXISTS demota;
- CREATE DATABASE demota;
- USE demota;
- DROP TABLE IF EXISTS tb_user_info;
- 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','','');
数据库B(192.168.8.150):
- DROP DATABASE IF EXISTS demota;
- CREATE DATABASE demota;
- USE demota;
- DROP TABLE IF EXISTS tb_account;
- 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);
2. 测试过程:
ps: 代码中模拟了异常出现的条件:如果账户金额<18000会抛出异常
JtaRunMainTest.java
- package michael.jta.atomikos;
- import michael.jta.atomikos.service.BankAccountService;
- import org.springframework.context.ApplicationContext;
- import org.springframework.context.support.ClassPathXmlApplicationContext;
- /**
- * @author michael
- *
- */
- public class JtaRunMainTest {
- /**
- * @param args
- */
- public static void main(String[] args) {
- System.out.println("------------start");
- ApplicationContext appCt = new ClassPathXmlApplicationContext(
- "jta.spring.xml");
- System.out.println("------------finished init xml");
- Object bean = appCt.getBean("bankAccountServiceProxy");
- System.out.println(bean.getClass());
- BankAccountService service = (BankAccountService) bean;
- service.doTestTransfer("husband", "wife", 2000);
- }
- }
运行第一次结果:
------------finished init xml
class $Proxy11
Hibernate: select bankaccoun0_.id as id2_, bankaccoun0_.deposit as deposit2_, bankaccoun0_.user_name as user3_2_ from tb_account bankaccoun0_ where bankaccoun0_.user_name=?
Hibernate: select bankaccoun0_.id as id2_, bankaccoun0_.deposit as deposit2_, bankaccoun0_.user_name as user3_2_ from tb_account bankaccoun0_ where bankaccoun0_.user_name=?
success done:husband transfer ¥2000.0 to wife
Hibernate: update tb_account set deposit=?, user_name=? where id=?
Hibernate: update tb_account set deposit=?, user_name=? where id=?
运行第二次结果:
------------finished init xml
class $Proxy11
Hibernate: select bankaccoun0_.id as id2_, bankaccoun0_.deposit as deposit2_, bankaccoun0_.user_name as user3_2_ from tb_account bankaccoun0_ where bankaccoun0_.user_name=?
Hibernate: select bankaccoun0_.id as id2_, bankaccoun0_.deposit as deposit2_, bankaccoun0_.user_name as user3_2_ from tb_account bankaccoun0_ where bankaccoun0_.user_name=?
Exception in thread "main" java.lang.RuntimeException: michael test exception for JTA
at michael.jta.atomikos.service.impl.BankAccountServiceImpl.doTestTransfer(BankAccountServiceImpl.java:51)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:299)
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:172)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:139)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:107)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:161)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:202)
at $Proxy11.doTestTransfer(Unknown Source)
at michael.jta.atomikos.JtaRunMainTest.main(JtaRunMainTest.java:26)
测试过程中数据库查询的结果截图:
从上面的数据库截图可见,第一正常运行时两个数据库同步更新了,第二次运行发生异常后,两个数据库的数据为发生变化,实现了事务回滚。
转载请注明来自:Michael's blog @ http://sjsky.iteye.com
-----------------------------------分 ------------------------------------隔 ------------------------------------线 --------------------------------------
- michael_jta_code.zip (10.1 KB)
- 下载次数: 379
- AtomikosTransactionsEssentials-3.7.0-lib.zip (946.5 KB)
- 下载次数: 285
- 分布式事务JTA之实践:Spring+ATOMIKOS
- 分布式事务JTA之实践:Spring+ATOMIKOS
- 分布式事务JTA之实践:Spring+ATOMIKOS
- Postgresql 分布式事务JTA实现Atomikos与Spring集成实践
- Atomikos Jta分布式事务spring集成实例
- Spring 3.0 + Atomikos构建jta分布式事务
- 分布式事务JTA实现之:SSM+ATOMIKOS
- Spring全局事务之JTA+Atomikos
- Spring全局事务之JTA+Atomikos
- Java事务(八) - 分布式事务 - spring + JTA + atomikos
- Java事务(八) - 分布式事务 - spring + JTA + atomikos
- Java事务之八——分布式事务(Spring+JTA+Atomikos+Hibernate+JMS)
- Java事务之八——分布式事务(Spring+JTA+Atomikos+Hibernate+JMS)
- JTA分布式事务实战(atomikos)
- 分布式事务入门例子(Spring+JTA+Atomikos+Hibernate+JMS)
- java分布式事务——spring+jta+atomikos
- 分布式事务入门-Spring+JTA+Atomikos+Hibernate+JMS
- 使用Spring和Atomikos集成JTA分布式事务
- Robotium 方法详细
- Microduino中LM75温度传感器的使用
- leetcode-first missing element
- OpenGL学习08_几何变换
- Java的类集框架一
- 分布式事务JTA之实践:Spring+ATOMIKOS
- 父窗体Form1 子窗体Form2
- 关于关于ActiveMQ的配置及优化
- C语言编程创建函数fork() 执行解析
- 初探swift语言的学习笔记九(OC与Swift混编)
- 【iOS-cocos2d-X 游戏开发之一】在Mac下结合Xcode搭建Cocos2d-X开发环境&卸载Cocos2dx模版
- flex动态效果
- TI DSP Function Structure and Calling Conventions
- hi3516A uboot 启动时的打印信息