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

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

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

demo将实现:

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

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

测试方式:restful web api

 

使用工具:

spring 4.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 @Service

15 public class NameService {

16     @Autowired

17     NameQaMapper nameQaMapper;

18     @Autowired

19     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 @Controller

15 public class mybatisController {

16 

17     @Autowired

18     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

 

 

 

 

 

 

 

 

 

 

 

你可能感兴趣的:(atomikos)