`
sjsky
  • 浏览: 917903 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

分布式事务JTA之实践:Spring+ATOMIKOS

阅读更多

   blog迁移至 :http://www.micmiu.com

 

在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);
    }
}
 

运行第一次结果:

写道
------------start
------------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=?

 运行第二次结果:

写道
------------start
------------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

-----------------------------------分 ------------------------------------隔 ------------------------------------线 --------------------------------------

 

 

 

 

 

 

  • 大小: 29.4 KB
6
3
分享到:
评论
5 楼 liyueling 2012-05-20  
很好的东东
4 楼 zjy7554 2012-04-20  
很好的资料,我一直在找这方面的东西,呵呵,搞了变天我终于调试通了,我是连接oracle
3 楼 sjsky 2011-07-01  
suhuanzheng7784877 写道
兄弟,什么时候将你的blog做成电子书啊~~~支持。。。。


没问题下次一定做的,谢谢支持噢
改版之后好像一个文章属于多个分类,做电子书时好像会重复的
2 楼 suhuanzheng7784877 2011-07-01  
兄弟,什么时候将你的blog做成电子书啊~~~支持。。。。
1 楼 吴小懒 2011-07-01  
 

相关推荐

    spring + JTA + atomikos 实现分布式事务

    Atomikos 是一个开源的JTA实现,它为Spring提供了一个轻量级、高性能的分布式事务解决方案。本文将深入探讨如何使用Spring、JTA和Atomikos来实现分布式事务。 首先,了解JTA的基本概念。JTA定义了一套规范,允许在...

    spring+druid+AtomikosDataSource实现多数据源切换及分布式事务控制

    Atomikos是著名的JTA(Java Transaction API)实现之一,提供了分布式事务管理的能力。AtomikosDataSource是基于Atomikos实现的XAPool,可以协调跨多个数据源的ACID(原子性、一致性、隔离性和持久性)事务。在...

    Spring Boot + Druid + Mybatis + Atomikos 配置多数据源 并支持分布式事务

    本教程将探讨如何利用Spring Boot、Druid、Mybatis以及Atomikos来配置多数据源并实现分布式事务。 首先,Spring Boot是Java生态系统中的一个流行框架,它简化了设置和配置过程,使得开发人员可以快速启动新项目。在...

    Spring+MyBatis+Atomikos实现JTA分布式事务

    本文将深入探讨如何使用Spring、MyBatis和Atomikos实现JTA分布式事务。 首先,让我们理解JTA的概念。Java Transaction API(JTA)是Java EE规范之一,用于处理分布式环境中的事务。它允许应用程序在不同的资源管理...

    spring+atomikos+druid分布式事务Demo

    总的来说,"spring+atomikos+druid分布式事务Demo"是一个学习和实践分布式事务处理的实用案例。通过理解和运行这个示例,开发者能够掌握如何在Spring环境中使用Atomikos和Druid实现高可用和一致性的分布式系统。

    分布式事务操作之Spring+JTA+mybatis源码

    - **Atomikos、Bitronix等JTA实现**:这些第三方库实现了JTA规范,可以用来管理分布式事务。 6. **配置Spring JTA事务**:在Spring的配置文件中,你需要配置 `UserTransaction` 和 `TransactionManager`,并启用...

    Spring boot+Atomikos+JTA+Hibernate+mybatis+MySQL实现分布式事务+多数据源

    本案例主要探讨如何利用Spring Boot、Atomikos、JTA(Java Transaction API)、Hibernate以及MyBatis,结合MySQL数据库,实现一个跨数据源的分布式事务解决方案。 首先,Spring Boot是一个基于Spring框架的快速开发...

    使用Spring+atomikos实现分布式事务

    Spring框架提供了强大的支持来处理分布式事务,而Atomikos是一个开源的事务管理器,专门用于处理JTA(Java Transaction API)事务,尤其适用于微服务和分布式环境。本教程将探讨如何结合Spring和Atomikos来实现...

    jta:Springboot + Atomikos + Druid + Mysql 实现JTA分布式事务

    jta Springboot + Atomikos + Druid + Mysql 实现JTA分布式事务 问题一:Druid 和 Mysql 版本的问题 问题二:Springtest 默认情况下事务回滚 Mysql对XA协议支持不好,有待看

    SpringBoot+Atomikos分布式事务及多数据源动态切换,两种demo

    Spring Boot作为轻量级的Java开发框架,结合Atomikos这样的分布式事务管理器,可以有效地解决这些问题。本文将深入探讨如何在Spring Boot项目中实现Atomikos分布式事务以及动态数据源切换的两种示例。 首先,我们...

    springboot+jta+atomikos

    本项目“springboot+jta+atomikos”旨在利用Spring Boot框架、Java Transaction API (JTA) 和 Atomikos 这一开源事务管理器来实现分布式事务的统一管理。下面将详细阐述这三个关键组件以及它们如何协同工作。 **...

    java+spring+mybatis+mysql+RuoYi-atomikos-实现分布式事务.zip

    在分布式系统中,确保数据的一致性和完整性是至关重要的,这就是分布式事务的职责所在。...对于理解和实践分布式事务,以及深入研究Spring Boot、MyBatis和Atomikos的开发者来说,这是一个宝贵的资源。

    Spring+SpringMVC+MyBatis+Atomikos整合

    本教程主要关注如何将Spring、SpringMVC、MyBatis和Atomikos这四个组件进行整合,以实现多数据源的分布式事务处理。 首先,Spring是Java领域广泛应用的依赖注入(DI)和面向切面编程(AOP)框架,它允许开发者轻松...

    第二部分spring+hibernate+jta 分布式事务Demo

    Spring、Hibernate和JTA(Java Transaction API)是Java开发者在构建分布式事务解决方案时常用的三大技术。本示例“第二部分spring+hibernate+jta 分布式事务Demo”将帮助我们理解如何在这三个框架之间协同工作,...

    Springboot+Atomikos+Jpa+Mysql实现JTA分布式事务

    本文将详细讲解如何利用Spring Boot、Atomikos、JPA(Java Persistence API)以及MySQL来实现JTA(Java Transaction API)分布式事务。 首先,Spring Boot是一个轻量级的框架,它简化了基于Spring的应用程序开发...

    基于Spring4.1.7+atomikos+mybaits 实现两阶段的分布式事务处理

    本文将深入探讨如何利用Spring 4.1.7、Atomikos以及MyBatis实现两阶段的分布式事务(2PC,Two-Phase Commit)。这是一套强大的解决方案,特别适用于处理跨多个数据库或服务的复杂业务逻辑。 首先,我们需要理解什么...

    spring+jpa+atomikos多数据源

    Atomikos是开源的事务处理服务提供商,特别适用于分布式事务处理。在3.9版本中,Atomikos提供了强大的JTA(Java Transaction API)支持,能够处理跨多个数据源的ACID(原子性、一致性、隔离性和持久性)事务。在多...

    Spring3.0+Hibernate+Atomikos多数据源分布式事务管理

    本教程将聚焦于如何利用Spring 3.0、Hibernate ORM框架以及Atomikos这个开源事务管理器来实现高效、可靠的多数据源分布式事务处理。 **Spring 3.0**: Spring是Java开发中最广泛使用的轻量级框架之一,它提供了一个...

    Spring boot+Atomikos+JTA+Hibernate+MySQL实现分布式事务+多数据源(可运行)

    本项目使用Spring Boot、Atomikos、JTA(Java Transaction API)、Hibernate和MySQL来实现分布式事务处理和多数据源管理,以确保在多个数据库操作之间保持事务的ACID特性。 首先,Spring Boot作为微服务开发的主流...

Global site tag (gtag.js) - Google Analytics