spring整合atomikos實現分佈式事務

前言

   Atomikos 是一個爲Java平臺提供增值服務的而且開源類事務管理器,主要用於處理跨數據庫事務,好比某個指令在A庫和B庫都有寫操做,業務上要求A庫和B庫的寫操做要具備原子性,這時候就能夠用到atomikos。筆者這裏整合了一個spring和atomikos的demo,而且經過案例演示說明atomikos的做用。java

準備工做

   開發工具:ideamysql

   數據庫:mysql , oraclegit

正文   

   源碼地址:github.com/qw870602/at…github

   演示原理:經過在兩個庫的寫操做之間人爲製造異常來觀察數據庫是否回滾web

   演示步驟:1.正常寫操做,觀察數據庫值的變化狀況spring

                    2.在寫操做語句之間製造異常,觀察數據庫值的變化狀況  sql

項目結構


從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模擬金額轉入spring-mvc

<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初始金額bash


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()方法,從日誌也證明了回滾成功。

相關文章
相關標籤/搜索