spring整合atomikos實(shí)現(xiàn)分布式事務(wù)的方法示例
前言
Atomikos 是一個(gè)為Java平臺(tái)提供增值服務(wù)的并且開(kāi)源類事務(wù)管理器,主要用于處理跨數(shù)據(jù)庫(kù)事務(wù),比如某個(gè)指令在A庫(kù)和B庫(kù)都有寫(xiě)操作,業(yè)務(wù)上要求A庫(kù)和B庫(kù)的寫(xiě)操作要具有原子性,這時(shí)候就可以用到atomikos。筆者這里整合了一個(gè)spring和atomikos的demo,并且通過(guò)案例演示說(shuō)明atomikos的作用。
準(zhǔn)備工作
開(kāi)發(fā)工具:idea
數(shù)據(jù)庫(kù):mysql , oracle
正文
源碼地址: https://github.com/qw870602/atomikos
演示原理:通過(guò)在兩個(gè)庫(kù)的寫(xiě)操作之間人為制造異常來(lái)觀察數(shù)據(jù)庫(kù)是否回滾
演示步驟:1.正常寫(xiě)操作,觀察數(shù)據(jù)庫(kù)值的變化情況
2.在寫(xiě)操作語(yǔ)句之間制造異常,觀察數(shù)據(jù)庫(kù)值的變化情況
項(xiàng)目結(jié)構(gòu)
從web.xml中可以知道,容器只加載了appliactionContext.xml,剩下的配置文件除了database.properties外都是無(wú)用文件,所以大家如果要在項(xiàng)目中配置的話,僅需要把a(bǔ)ppliactionContext.xml中關(guān)于atomikos的部分新增到自己項(xiàng)目中就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"> <!-- 引入數(shù)據(jù)源信息的properties屬性文件 --> <context:property-placeholder location="classpath:database.properties" /> <!-- XA方式 --> <!-- MYSQL數(shù)據(jù)庫(kù)配置 --> <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數(shù)據(jù)庫(kù)配置 --> <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> <!-- 分布式事務(wù) --> <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/> <!--<!– 自動(dòng)掃描controller包下的所有類,如果@Controller注入為bean –>--> <!--<!–事務(wù)管理層–>--> <context:component-scan base-package="com.xy" /> <!-- 注冊(cè)攔截器 --> <!--<mvc:interceptors> <bean class="com.springmybatis.system.interceptor.MyInterceptor" /> </mvc:interceptors>--> </beans>
適用JUnit4進(jìn)行單元測(cè)試
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; /** * 在同一事務(wù)有多個(gè)數(shù)據(jù)源 */ @Test public void multipleDataSource2() { transactionTestService.updateMultipleDataSource("1","1", 100L,"1.6"); } }
業(yè)務(wù)實(shí)現(xiàn),當(dāng)前沒(méi)有異常操作
@Service public class TransactionTestServiceImpl implements TransactionTestService { @Autowired @Qualifier("mysqlTransactionTestDao") private MysqlTransactionTestDao mysqlTransactionTestDao; @Autowired @Qualifier("transactionTestDao") private TransactionTestDao transactionTestDao; /** * 在同一事務(wù)有多個(gè)數(shù)據(jù)源 */ @Override @Transactional public void updateMultipleDataSource(String deUserId, String inUserid, long money,String str) { // 賬戶1轉(zhuǎn)出操作 mysqlTransactionTestDao.decreaseMoney(deUserId, money); //Integer.parseInt(str); // 賬戶2轉(zhuǎn)入操作 transactionTestDao.increaseMoney(inUserid, money); } }
mysql模擬金額轉(zhuǎn)出,oracle模擬金額轉(zhuǎn)入
<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初始金額
執(zhí)行正常操作
mysql當(dāng)前金額
oracle當(dāng)前金額
將被屏蔽的制造異常的代碼打開(kāi)
public void updateMultipleDataSource(String deUserId, String inUserid, long money,String str) { // 賬戶1轉(zhuǎn)出操作 mysqlTransactionTestDao.decreaseMoney(deUserId, money); Integer.parseInt("skg"); // 賬戶2轉(zhuǎn)入操作 transactionTestDao.increaseMoney(inUserid, money); }
發(fā)現(xiàn)mysql和oracle的當(dāng)前金額都沒(méi)有變化,說(shuō)明事務(wù)回滾成功,查看日志
發(fā)現(xiàn)控制臺(tái)打印出了異常信息,并且atomikos調(diào)用了rollback()方法,從日志也證實(shí)了回滾成功。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
SpringCloud如何引用xxjob定時(shí)任務(wù)
Spring?Cloud?本身不直接支持?XXL-JOB?這樣的定時(shí)任務(wù)框架,如果你想在?Spring?Cloud?應(yīng)用中集成?XXL-JOB,你需要手動(dòng)進(jìn)行配置,本文給大家介紹SpringCloud如何引用xxjob定時(shí)任務(wù),感興趣的朋友一起看看吧2024-04-04Java抓包工具fiddler實(shí)現(xiàn)請(qǐng)求轉(zhuǎn)發(fā)
Fiddler是一個(gè)http協(xié)議調(diào)試代理工具,本文主要介紹了Java抓包工具fiddler實(shí)現(xiàn)請(qǐng)求轉(zhuǎn)發(fā),文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-04-04詳解Spring中Spel表達(dá)式和el表達(dá)式的區(qū)別
在?Java?開(kāi)發(fā)中,表達(dá)式語(yǔ)言是一種強(qiáng)大的工具,而SpEL?表達(dá)式與EL?表達(dá)式是我們常常遇到兩種表達(dá)式語(yǔ)言,下面我們就來(lái)看看它們的具體使用與區(qū)別吧2023-07-07Java 時(shí)間格式轉(zhuǎn)換之impleDateFormat與Data API解析與使用
想必大家對(duì) SimpleDateFormat 并不陌生。SimpleDateFormat 是 Java 中一個(gè)非常常用的類,他是以區(qū)域敏感的方式格式化和解析日期的具體類。 它允許格式化 (date -> text)、語(yǔ)法分析 (text -> date)和標(biāo)準(zhǔn)化2021-11-11詳解rabbitmq創(chuàng)建queue時(shí)arguments參數(shù)注釋
這篇文章主要介紹了rabbitmq創(chuàng)建queue時(shí)arguments參數(shù)注釋,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-03-03Java如何使用SSLContext請(qǐng)求https鏈接
這篇文章主要介紹了Java如何使用SSLContext請(qǐng)求https鏈接問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-01-01