zoukankan      html  css  js  c++  java
  • 使用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 两个数据库数据都未添加

    源码下载:https://files.cnblogs.com/files/rain-in-sun/springmvc-mybatis-atomikos.rar

     

     

  • 相关阅读:
    SpringMVC后台token防重复提交解决方案
    浅析微信支付:开通免充值产品功能及如何进行接口升级指引
    浅析微信支付:查询订单和关闭订单
    浅析微信支付:商户平台代金券或立减优惠开通、指定用户代金券发放、查询等
    linux内核源码分析
    linux内存源码分析
    linux内存源码分析
    linux内存源码分析
    linux内存源码分析
    linux内存源码分析
  • 原文地址:https://www.cnblogs.com/rain-in-sun/p/4310296.html
Copyright © 2011-2022 走看看