欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

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

程序员文章站 2024-02-23 23:52:23
前言 atomikos 是一个为java平台提供增值服务的并且开源类事务管理器,主要用于处理跨数据库事务,比如某个指令在a库和b库都有写操作,业务上要求a库和b库的写操作...

前言

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

准备工作

开发工具:idea

数据库:mysql , oracle

正文

源码地址:

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

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

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

项目结构

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

从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/>
  <!--<!– 自动扫描controller包下的所有类,如果@controller注入为bean –>-->
  <!--<!–事务管理层–>-->
  <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初始金额

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

oracle初始金额

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

执行正常操作

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

mysql当前金额

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

oracle当前金额

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

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

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的当前金额都没有变化,说明事务回滚成功,查看日志

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

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

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