使用spring+mybatis+atomikos+tomcat构建分布式事务

9/6/2015来源:Java教程人气:2571

使用sPRing+mybatis+atomikos+tomcat构建分布式事务

本文通过一个demo,介绍如何使用spring+mybatis+atomikos+tomcat构建在一个事务中涉及两个数据源的web应用。

demo功能:实现一个能成功提交和回滚的涉及两个数据库数据源的XA事务。

demo将实现:

1.一次性在两个数据库的两张表中各插入一条数据并提交。

2.一次性在两个数据库的两张表中各插入一条数据并回滚。

测试方式:restful web api

使用工具:

spring4.1.1.RELEASE

mybatis 3.2.7

atomikos 3.7.0

tomcat 7

MySQL中建立两个schema,分别为dev和qa。并在里面分别建立一张名字表。

schema:dev

table:namaDev

id | nameDev

scheme:qa

table:nameQa

id | nameQa

对应的sql为

 1 CREATE SCHEMA `qa` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci ; 2 CREATE SCHEMA `dev` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci ; 3  4  CREATE  TABLE `dev`.`nameDev` ( 5   `id` BIGINT NOT NULL AUTO_INCREMENT , 6   `nameDev` VARCHAR(45) NULL , 7   PRIMARY KEY (`id`) , 8   UNIQUE INDEX `id_UNIQUE` (`id` ASC) ); 9 10   CREATE  TABLE `qa`.`nameQa` (11   `id` BIGINT NOT NULL AUTO_INCREMENT ,12   `nameQa` VARCHAR(45) NULL ,13   PRIMARY KEY (`id`) ,14   UNIQUE INDEX `id_UNIQUE` (`id` ASC) );

代码分析:

本项目使用spring框架,因此首先配置相关bean

  1 <?xml version="1.0" encoding="UTF-8"?>  2 <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  3        xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context"  4        xmlns:tx="http://www.springframework.org/schema/tx" xmlns:jdbc="http://www.springframework.org/schema/jdbc"  5        xmlns:rabbit="http://www.springframework.org/schema/rabbit"  6        xmlns:cache="http://www.springframework.org/schema/cache" xmlns:task="http://www.springframework.org/schema/task"  7        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd http://www.springframework.org/schema/rabbit http://www.springframework.org/schema/rabbit/spring-rabbit.xsd  8        http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd">  9     <context:component-scan base-package="com.xy"> 10         <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/> 11     </context:component-scan> 12     <context:property-placeholder location="classpath:context/database.properties"/> 13     <tx:annotation-driven/> 14  15     <bean id="abstractXADataSource" class="com.atomikos.jdbc.AtomikosDataSourceBean" init-method="init" 16           destroy-method="close" abstract="true"> 17         <property name="xaDataSourceClassName" value="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource"/> 18         <property name="poolSize" value="10" /> 19         <property name="minPoolSize" value="10"/> 20         <property name="maxPoolSize" value="30"/> 21         <property name="borrowConnectionTimeout" value="60"/> 22         <property name="reapTimeout" value="20"/> 23         <!-- 最大空闲时间 --> 24         <property name="maxIdleTime" value="60"/> 25         <property name="maintenanceInterval" value="60"/> 26         <property name="loginTimeout" value="60"/> 27         <property name="testQuery"> 28             <value>select 1</value> 29         </property> 30     </bean> 31      32     <bean id="qadataSource" parent="abstractXADataSource"> 33         <!-- value只要两个数据源不同就行,随便取名 --> 34         <property name="uniqueResourceName" value="mysql/sitestone1" /> 35         <property name="xaDataSourceClassName" 36                   value="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource" /> 37         <property name="xaProperties"> 38             <props> 39                 <prop key="URL">${qa.db.url}</prop> 40                 <prop key="user">${qa.db.user}</prop> 41                 <prop key="passWord">${qa.db.password}</prop> 42                 <prop key="pinGlobalTxToPhysicalConnection">true</prop> 43             </props> 44         </property> 45     </bean> 46  47     <bean id="devdataSource" parent="abstractXADataSource"> 48         <!-- value只要两个数据源不同就行,随便取名 --> 49         <property name="uniqueResourceName" value="mysql/sitestone" /> 50         <property name="xaDataSourceClassName" 51                   value="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource" /> 52         <property name="xaProperties"> 53             <props> 54                 <prop key="URL">${dev.db.url}</prop> 55                 <prop key="user">${dev.db.user}</prop> 56                 <prop key="password">${dev.db.password}</prop> 57                 <prop key="pinGlobalTxToPhysicalConnection">true</prop> 58             </props> 59         </property> 60     </bean> 61  62  63  64     <bean id="qasqlsessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> 65         <property name="dataSource" ref="qadataSource" /> 66         <property name="mapperLocations" value="classpath*:com/xy/dao/*.xml" /> 67     </bean> 68  69     <bean id="devsqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> 70         <property name="dataSource" ref="devdataSource" /> 71         <property name="mapperLocations" value="classpath*:com/xy/daodev/*.xml" /> 72     </bean> 73  74     <bean id="atomikosTransactionManager" class="com.atomikos.icatch.jta.UserTransactionManager" 75           init-method="init" destroy-method="close"> 76         <property name="forceShutdown"> 77             <value>true</value> 78         </property> 79     </bean> 80     <bean id="atomikosUserTransaction" class="com.atomikos.icatch.jta.UserTransactionImp"> 81         <property name="transactionTimeout" value="300" /> 82     </bean> 83  84     <bean id="transactionManager" 85           class="org.springframework.transaction.jta.JtaTransactionManager"> 86         <property name="transactionManager"> 87             <ref bean="atomikosTransactionManager"/> 88         </property> 89         <property name="userTransaction"> 90             <ref bean="atomikosUserTransaction"/> 91         </property> 92         <!-- 必须设置,否则程序出现异常 JtaTransactionManager does not support custom isolation levels by default --> 93         <property name="allowCustomIsolationLevels" value="true"/> 94  95     </bean> 96  97  98  99     <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">100         <property name="basePackage" value="com.xy.dao"/>101         <property name="sqlSessionFactoryBeanName" value="qasqlSessionFactory" />102     </bean>103 104     <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">105         <property name="basePackage" value="com.xy.daodev"/>106         <property name="sqlSessionFactoryBeanName" value="devsqlSessionFactory" />107     </bean>108 </beans>

其中qadataSource和devdataSource是对应两个数据库的数据源,qasqlSessionFactory和devsqlSessionFactory是mybatis的sessionfactory,两个MapperScannerConfigurer自动将不同数据源的sql语句文件与interface自动装配起来,atomikosTransactionManager会自动管理两个atomikos的数据源的事务,即resource manager,atomikosUserTransaction为最上层的事务管理器为transaction manager。(关于RM和TM,请参见上篇博文)。

Model类如下:package com.xy.model

 1 package com.xy.model; 2  3 /** 4  * Created by helloworld on 2015/1/30. 5  */ 6 public class NameQa { 7     private long id; 8     private String nameQa; 9 10     public long getId() {11         return id;12     }13 14     public void setId(long id) {15         this.id = id;16     }17 18     public String getNameQa() {19         return nameQa;20     }21 22     public void setNameQa(String nameQa) {23         this.nameQa = nameQa;24     }25 }
nameQa class

 1 package com.xy.model; 2  3 /** 4  * Created by helloworld on 2015/1/30. 5  */ 6 public class NameDev { 7     private long id; 8     private String nameDev; 9 10     public long getId() {11         return id;12     }13 14     public void setId(long id) {15         this.id = id;16     }17 18     public String getNameDev() {19         return nameDev;20     }21 22     public void setNameDev(String nameDev) {23         this.nameDev = nameDev;24     }25 }
nameDev class

qa数据源的mybatis mapper接口 package com.xy.dao

 1 package com.xy.dao; 2  3 import com.xy.model.NameQa; 4  5 /** 6  * Created by helloworld on 2015/1/30. 7  */ 8 public interface NameQaMapper { 9     int insert(NameQa nameQa);10 }
NameQaMapper

dev数据源的mybatis mapper接口 package com.xy.devdao

 1 package com.xy.daodev; 2  3 import com.xy.model.NameDev; 4  5 /** 6  * Created by helloworld on 2015/1/30. 7  */ 8 public interface NameDevMapper { 9     int insert(NameDev nameDev);10 }
NameDevMapper

处理事务的service

 1 package com.xy.service; 2  3 import com.xy.dao.NameQaMapper; 4 import com.xy.daodev.NameDevMapper; 5 import com.xy.model.NameDev; 6 import com.xy.model.NameQa; 7 import org.springframework.beans.factory.annotation.Autowired; 8 import org.springframework.stereotype.Service; 9 import org.springframework.transaction.annotation.Transactional;10 11 /**12  * Created by helloworld on 2015/1/30.13  */14 @Service15 public class NameService {16     @Autowired17     NameQaMapper nameQaMapper;18     @Autowired19     NameDevMapper nameDevMapper;20 21     @Transactional(rollbackFor = Exception.class)22     public void addQaAndDev(boolean hasException) throws Exception {23         NameQa nameQa = new NameQa();24         nameQa.setNameQa("qa");25         nameQaMapper.insert(nameQa);26 27         NameDev nameDev = new NameDev();28         nameDev.setNameDev("dev");29         nameDevMapper.insert(nameDev);30 31         if(hasException) {32             throw new Exception();33         }34     }35 36 37 }
nameservice

controller代码

 1 package com.xy.controller; 2  3 import com.xy.service.NameService; 4 import org.springframework.beans.factory.annotation.Autowired; 5 import org.springframework.stereotype.Controller; 6 import org.springframework.ui.ModelMap; 7 import org.springframework.web.bind.annotation.RequestMapping; 8 import org.springframework.web.bind.annotation.RequestMethod; 9 import org.springframework.web.bind.annotation.RequestParam;10 11 /**12  * Created by helloworld on 2014/11/22.13  */14 @Controller15 public class mybatisController {16 17     @Autowired18     NameService nameService;19 20     @RequestMapping(value = "/addName", method = RequestMethod.POST)21     ModelMap addName(@RequestParam("hasException") boolean hasException) {22         try {23             nameService.addQaAndDev(hasException);24         } catch (Exception e) {25             e.printStackTrace();26             return new ModelMap("false");27         }28         return new ModelMap("true");29     }30 31 32 }
controller

将项目打成war包,命名为mybatis.war部署在tomcat上。

测试:

1.POST http://localhost:8080/mybatis/addName.json

request parameters: hasException=false返回:true 数据添加成功

2.POST http://localhost:8080/mybatis/addName.json

 request parameters: hasException=true

返回:false 两个数据库数据都未添加源码下载:http://files.cnblogs.com/files/rain-in-sun/springmvc-mybatis-atomikos.rar