spring整合atomikos实现分布式事务的方法示例

 更新时间:2019年05月21日 09:26:07   作者:胖子k  
本文整合了一个spring和atomikos的demo,并且通过案例演示说明atomikos的作用,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

前言

Atomikos 是一个为Java平台提供增值服务的并且开源类事务管理器,主要用于处理跨数据库事务,比如某个指令在A库和B库都有写操作,业务上要求A库和B库的写操作要具有原子性,这时候就可以用到atomikos。笔者这里整合了一个spring和atomikos的demo,并且通过案例演示说明atomikos的作用。

准备工作

开发工具:idea

数据库:mysql , oracle

正文

源码地址: https://github.com/qw870602/atomikos

演示原理:通过在两个库的写操作之间人为制造异常来观察数据库是否回滚

演示步骤:1.正常写操作,观察数据库值的变化情况

                   2.在写操作语句之间制造异常,观察数据库值的变化情况

项目结构

从web.xml中可以知道,容器只加载了appliactionContext.xml,剩下的配置文件除了database.properties外都是无用文件,所以大家如果要在项目中配置的话,仅需要把appliactionContext.xml中关于atomikos的部分新增到自己项目中就OK了

appliactionContext.xml

<?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:database.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.qa.db.url}</prop>
        <prop key="user">${mysql.qa.db.user}</prop>
        <prop key="password">${mysql.qa.db.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">${oracle.qa.db.url}</prop>
        <prop key="user">${oracle.qa.db.user}</prop>
        <prop key="password">${oracle.qa.db.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" />
    <property name="mapperLocations" >
      <list>
        <value>classpath*:/dao/*.xml</value>
      </list>
    </property>
  </bean>
  <bean id="sqlSessionFactoryOracle" class="org.mybatis.spring.SqlSessionFactoryBean">
    <!--<property name="configLocation" value="classpath:mybatis-config.xml" />-->
    <property name="dataSource" ref="oracleDataSource" />
    <property name="mapperLocations" >
      <list>
        <value>classpath*:/daodev/*.xml</value>
      </list>
    </property>
  </bean>

  <!-- MyBatis为不同的mapper注入sqlSessionFactory -->
  <bean id="mysqlTransactionTestDao" class="org.mybatis.spring.mapper.MapperFactoryBean">
    <property name="sqlSessionFactory" ref="sqlSessionFactory" />
    <property name="mapperInterface" value="com.xy.dao.MysqlTransactionTestDao" />
  </bean>
  <bean id="transactionTestDao" class="org.mybatis.spring.mapper.MapperFactoryBean">
    <property name="sqlSessionFactory" ref="sqlSessionFactoryOracle" />
    <property name="mapperInterface" value="com.xy.dao.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/>
  <!--&lt;!&ndash; 自动扫描controller包下的所有类,如果@Controller注入为bean &ndash;&gt;-->
  <!--&lt;!&ndash;事务管理层&ndash;&gt;-->
  <context:component-scan base-package="com.xy" />

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

适用JUnit4进行单元测试

package com.xy.controller;

import com.xy.daodev.TransactionTestService;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.AbstractJUnit4SpringContextTests;

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

业务实现,当前没有异常操作

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

}

mysql模拟金额转出,oracle模拟金额转入

<update id="decreaseMoney" parameterType="java.util.Map">
  UPDATE fx1 SET amount=amount - #{1,jdbcType=BIGINT} WHERE id=#{0,jdbcType=VARCHAR}
</update>
<update id="increaseMoney">
  UPDATE fx1 SET amount=amount + #{1,jdbcType=BIGINT} WHERE id=#{0,jdbcType=VARCHAR}
</update>

mysql初始金额

oracle初始金额

执行正常操作

mysql当前金额

oracle当前金额

将被屏蔽的制造异常的代码打开

public void updateMultipleDataSource(String deUserId, String inUserid, long money,String str) {
 // 账户1转出操作
 mysqlTransactionTestDao.decreaseMoney(deUserId, money);
  Integer.parseInt("skg");
 // 账户2转入操作
 transactionTestDao.increaseMoney(inUserid, money);
} 

发现mysql和oracle的当前金额都没有变化,说明事务回滚成功,查看日志

发现控制台打印出了异常信息,并且atomikos调用了rollback()方法,从日志也证实了回滚成功。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

相关文章

  • Java软件设计模式之适配器模式详解

    Java软件设计模式之适配器模式详解

    这篇文章主要介绍了Java软件设计模式之适配器模式详解,适配器模式可分为对象适配器和类适配器两种,在对象适配器模式中,适配器与适配者之间是关联关系;在类适配器模式中,适配器与适配者之间是继承(或实现)关系,需要的朋友可以参考下
    2023-07-07
  • JavaWeb编程 Servlet的基本配置

    JavaWeb编程 Servlet的基本配置

    本文讲的是Servlet最基本的配置信息,相信对你一定有帮助
    2013-11-11
  • SpringCloud如何引用xxjob定时任务

    SpringCloud如何引用xxjob定时任务

    Spring Cloud 本身不直接支持 XXL-JOB 这样的定时任务框架,如果你想在 Spring Cloud 应用中集成 XXL-JOB,你需要手动进行配置,本文给大家介绍SpringCloud如何引用xxjob定时任务,感兴趣的朋友一起看看吧
    2024-04-04
  • Java抓包工具fiddler实现请求转发

    Java抓包工具fiddler实现请求转发

    Fiddler是一个http协议调试代理工具,本文主要介绍了Java抓包工具fiddler实现请求转发,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-04-04
  • MyBatis一对多嵌套查询的完整实例

    MyBatis一对多嵌套查询的完整实例

    这篇文章主要给大家介绍了关于MyBatis一对多嵌套查询的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-01-01
  • Java引用队列和虚引用实例分析

    Java引用队列和虚引用实例分析

    这篇文章主要介绍了Java引用队列和虚引用,结合实例形式分析了java引用队列和虚引用相关概念、原理与使用方法,需要的朋友可以参考下
    2019-08-08
  • 详解Spring中Spel表达式和el表达式的区别

    详解Spring中Spel表达式和el表达式的区别

    在 Java 开发中,表达式语言是一种强大的工具,而SpEL 表达式与EL 表达式是我们常常遇到两种表达式语言,下面我们就来看看它们的具体使用与区别吧
    2023-07-07
  • Java 时间格式转换之impleDateFormat与Data API解析与使用

    Java 时间格式转换之impleDateFormat与Data API解析与使用

    想必大家对 SimpleDateFormat 并不陌生。SimpleDateFormat 是 Java 中一个非常常用的类,他是以区域敏感的方式格式化和解析日期的具体类。 它允许格式化 (date -> text)、语法分析 (text -> date)和标准化
    2021-11-11
  • 详解rabbitmq创建queue时arguments参数注释

    详解rabbitmq创建queue时arguments参数注释

    这篇文章主要介绍了rabbitmq创建queue时arguments参数注释,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-03-03
  • Java如何使用SSLContext请求https链接

    Java如何使用SSLContext请求https链接

    这篇文章主要介绍了Java如何使用SSLContext请求https链接问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-01-01

最新评论