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

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

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

demo将实现:

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

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

测试方式:restful web api

使用工具:

spring 4.1.1.RELEASE

hibernate 4.2.4.Final

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) );

create table

代码分析:

本项目使用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:property-placeholder file-encoding="UTF-8" ignore-resource-not-found="true"
 10                                   location="classpath*:context/database.properties"/>
 11     <context:component-scan base-package="com.xy">
 12         <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
 13     </context:component-scan>
 14     <tx:annotation-driven/>
 15
 16
 17
 18     <bean id="abstractXADataSource" class="com.atomikos.jdbc.AtomikosDataSourceBean" init-method="init"
 19           destroy-method="close" abstract="true">
 20         <property name="xaDataSourceClassName" value="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource"/>
 21         <property name="poolSize" value="10" />
 22         <property name="minPoolSize" value="10"/>
 23         <property name="maxPoolSize" value="30"/>
 24         <property name="borrowConnectionTimeout" value="60"/>
 25         <property name="reapTimeout" value="20"/>
 26         <!-- 最大空闲时间 -->
 27         <property name="maxIdleTime" value="60"/>
 28         <property name="maintenanceInterval" value="60"/>
 29         <property name="loginTimeout" value="60"/>
 30         <property name="testQuery">
 31             <value>select 1</value>
 32         </property>
 33     </bean>
 34
 35     <bean id="qadataSource" parent="abstractXADataSource">
 36         <!-- value只要两个数据源不同就行,随便取名 -->
 37         <property name="uniqueResourceName" value="mysql/sitestone" />
 38         <property name="xaDataSourceClassName"
 39                   value="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource" />
 40         <property name="xaProperties">
 41             <props>
 42                 <prop key="URL">${qa.db.url}</prop>
 43                 <prop key="user">${qa.db.user}</prop>
 44                 <prop key="password">${qa.db.password}</prop>
 45                 <prop key="pinGlobalTxToPhysicalConnection">true</prop>
 46             </props>
 47         </property>
 48     </bean>
 49
 50     <bean id="devdataSource" parent="abstractXADataSource">
 51         <!-- value只要两个数据源不同就行,随便取名 -->
 52         <property name="uniqueResourceName" value="mysql/sitestone1" />
 53         <property name="xaDataSourceClassName"
 54                   value="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource" />
 55         <property name="xaProperties">
 56             <props>
 57                 <prop key="URL">${dev.db.url}</prop>
 58                 <prop key="user">${dev.db.user}</prop>
 59                 <prop key="password">${dev.db.password}</prop>
 60                 <prop key="pinGlobalTxToPhysicalConnection">true</prop>
 61             </props>
 62         </property>
 63     </bean>
 64
 65     <bean id="qaSessionFactory"
 66                  class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
 67         <property name="packagesToScan" value="com.xy.model"/>
 68         <property name="dataSource" ref="qadataSource"/>
 69         <property name="hibernateProperties">
 70             <props>
 71                 <prop key="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</prop>
 72                 <prop key="hibernate.show_sql">false</prop>
 73                 <prop key="hibernate.autoReconnect">true</prop>
 74                 <prop key="hibernate.connection.release_mode">after_transaction</prop>
 75             </props>
 76         </property>
 77     </bean>
 78     <bean id="devSessionFactory"
 79           class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
 80         <property name="packagesToScan" value="com.xy.model"/>
 81         <property name="dataSource" ref="devdataSource"/>
 82         <property name="hibernateProperties">
 83             <props>
 84                 <prop key="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</prop>
 85                 <prop key="hibernate.show_sql">false</prop>
 86                 <prop key="hibernate.autoReconnect">true</prop>
 87                 <prop key="hibernate.connection.release_mode">after_transaction</prop>
 88             </props>
 89         </property>
 90     </bean>
 91
 92     <bean id="atomikosTransactionManager" class="com.atomikos.icatch.jta.UserTransactionManager"
 93           init-method="init" destroy-method="close">
 94         <property name="forceShutdown">
 95             <value>true</value>
 96         </property>
 97     </bean>
 98     <bean id="atomikosUserTransaction" class="com.atomikos.icatch.jta.UserTransactionImp">
 99         <property name="transactionTimeout" value="300" />
100     </bean>
101
102     <bean id="transactionManager"
103           class="org.springframework.transaction.jta.JtaTransactionManager">
104         <property name="transactionManager">
105             <ref bean="atomikosTransactionManager"/>
106         </property>
107         <property name="userTransaction">
108             <ref bean="atomikosUserTransaction"/>
109         </property>
110         <!-- 必须设置,否则程序出现异常 JtaTransactionManager does not support custom isolation levels by default -->
111         <property name="allowCustomIsolationLevels" value="true"/>
112
113     </bean>
114
115 </beans>

其中qadataSource和devdataSource是对应两个数据库的数据源,qaSessionFactory和devSessionFactory是hibernate的sessionfactory。atomikosTransactionManager会自动管理两个atomikos的数据源的事务,即resource manager,atomikosUserTransaction为最上层的事务管理器为transaction manager。(关于RM和TM,请参见上篇博文)。

Model类如下:package com.xy.model

package com.xy.model;

import javax.persistence.*;

/**
 * Created by helloworld on 2015/1/30.
 */
@Entity
@Table(name = "nameDev")
public class NameDev {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private long id;
    private String nameDev;

    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public String getNameDev() {
        return nameDev;
    }

    public void setNameDev(String nameDev) {
        this.nameDev = nameDev;
    }
}

NameDev

 1 package com.xy.model;
 2
 3
 4 import javax.persistence.*;
 5
 6 /**
 7  * Created by helloworld on 2015/1/30.
 8  */
 9 @Entity
10 @Table(name = "nameQa")
11 public class NameQa {
12     @Id
13     @GeneratedValue(strategy = GenerationType.AUTO)
14     private long id;
15     private String nameQa;
16
17     public long getId() {
18         return id;
19     }
20
21     public void setId(long id) {
22         this.id = id;
23     }
24
25     public String getNameQa() {
26         return nameQa;
27     }
28
29     public void setNameQa(String nameQa) {
30         this.nameQa = nameQa;
31     }
32 }

nameQa

处理事务的service

 1 package com.xy.service;
 2
 3 import com.xy.model.NameDev;
 4 import com.xy.model.NameQa;
 5 import org.hibernate.SessionFactory;
 6 import org.springframework.beans.factory.annotation.Autowired;
 7 import org.springframework.stereotype.Service;
 8 import org.springframework.transaction.annotation.Transactional;
 9
10 /**
11  * Created by helloworld on 2015/1/30.
12  */
13 @Service
14 public class NameService {
15     @Autowired
16     private SessionFactory qaSessionFactory;
17     @Autowired
18     private SessionFactory devSessionFactory;
19
20     @Transactional(rollbackFor = Exception.class)
21     public void addQaAndDev(boolean hasException) throws Exception {
22         NameQa nameQa = new NameQa();
23         nameQa.setNameQa("hello");
24         qaSessionFactory.getCurrentSession().save(nameQa);
25
26         NameDev nameDev = new NameDev();
27         nameDev.setNameDev("hello");
28         devSessionFactory.getCurrentSession().save(nameDev);
29
30         if(hasException){
31             throw new Exception();
32         }
33
34     }
35
36
37 }

service

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 /**
13  * Created by helloworld on 2014/11/22.
14  */
15 @Controller
16 public class hibernateController {
17     @Autowired
18     NameService nameService;
19
20
21     @RequestMapping(value = "/addName", method = RequestMethod.POST)
22     ModelMap addName(@RequestParam("hasException") boolean hasException){
23         try {
24             nameService.addQaAndDev(hasException);
25         } catch (Exception e) {
26             e.printStackTrace();
27             return new ModelMap("false");
28         }
29         return new ModelMap("true");
30     }
31
32
33 }

controller

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

测试:

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

request parameters: hasException=false

返回:true 数据添加成功

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

 request parameters: hasException=true

返回:false 两个数据库数据都未添加

源码下载:http://files.cnblogs.com/files/rain-in-sun/springmvc-hibernate-atomikos.rar
时间: 2024-10-08 11:57:38

使用spring+hibernate+atomikos+tomcat构建分布式事务的相关文章

使用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 t

基于spring+mybatis+atomikos+jta实现分布式事务(2)-动态切换数据源

本文介绍基于spring+mybatis+atomikos+jta实现分布式事务,由程序动态切换数据源,通过atomikos可实现分布式事务一致性. 版本:spring-3.2.9.RELEASE.mybatis-3.4.4.atomikos-4.0.5.jdk1.8 1,maven配置文件pom.xml如下: <!-- test --> <dependency> <groupId>junit</groupId> <artifactId>juni

使用Atomikos实现JTA分布式事务

使用Atomikos实现JTA分布式事务 在这篇文章中: 1.1 JTA事务模型 2 JTA规范接口 3 Atomikos分布式事务 本文全面的介绍了JTA分布式事务模型和接口规范,以及开源的分布式事务解决方案Atomikos.笔者认同"talk is cheap,show me the code",因此在文章最后,给出一个完整的Atomikos与spring.mybatis整合的完整案例. 1 JTA规范 Java事务API(JTA:Java Transaction API)和它的同

Spring Cloud Alibaba | 微服务分布式事务之Seata

Spring Cloud Alibaba | 微服务分布式事务之Seata 本篇实战所使用Spring有关版本: SpringBoot:2.1.7.RELEASE Spring Cloud:Greenwich.SR2 Spring CLoud Alibaba:2.1.0.RELEASE 1. 概述 在构建微服务的过程中,不管是使用什么框架.组件来构建,都绕不开一个问题,跨服务的业务操作如何保持数据一致性. 2. 什么是分布式事务? 首先,设想一个传统的单体应用,无论多少内部调用,最后终归是在同一

【分布式事务】使用atomikos+jta解决分布式事务问题

一.前言 分布式事务,这个问题困惑了小编很久,在3个月之前,就间断性的研究分布式事务.从MQ方面,数据库事务方面,jta方面.近期终于成功了,使用JTA解决了分布式事务问题.先写一下心得,后面的二级提交也会在研究. 二.介绍 分布式事务 说到分布式事务,可以理解为,由于分布式而引起的事务不一致的问题.随着项目做大,模块拆分,数据库拆分.一次包含增删改操作数据库涉及到了更新两个不同物理节点的数据库,这样的数据库事务只能保证自己处理的部分的事务,但是整个的事务就不能保证一致性. 网上针对分布式事务常

Spring Boot 集成 Seata 解决分布式事务问题

文章首发于公众号<程序员果果>地址 : https://mp.weixin.qq.com/s/aDhGG3Y2t4lPYetK01Wmxg seata 简介 Seata 是 阿里巴巴2019年开源的分布式事务解决方案,致力于在微服务架构下提供高性能和简单易用的分布式事务服务.在 Seata 开源之前,Seata 对应的内部版本在阿里内部一直扮演着分布式一致性中间件的角色,帮助阿里度过历年的双11,对各业务进行了有力的支撑.经过多年沉淀与积累,2019.1 Seata 正式宣布对外开源 .目前

spring分布式事务学习笔记

最近项目中使用了分布式事务,本文及接下来两篇文章总结一下在项目中学到的知识. 分布式事务对性能有一定的影响,所以不是最佳的解决方案,能通过设计避免最好尽量避免. 分布式事务(Distributed transactions),也称作XA事务(XA是一个协议的名字),在spring中被称作global transaction,是指一个事务会涉及到不同的事务资源,比如不同的数据库,消息队列.事务资源都支持commit和rollback这样的事务控制命令. 按是否需要实现完整javaEE功能的应用服务

spring+springMVC+Mybatis架构下采用AbstractRoutingDataSource、atomikos、JTA实现多数据源灵活切换以及分布式事务管理

背景: 1.系统采用SSM架构.需要在10多个MYSQL数据库之间进行切换并对数据进行操作,上篇博文<springMVC+Mybatis(使用AbstractRoutingDataSource实现多数据源切换时)事务管理未生效的解决办法> 2.第一步先通过AbstractRoutingDataSource实现了多数据源的灵活切换,但是后来发现事务不起作用: 3.发现问题是因为重复扫描service包导致第二次扫入容器的BEAN没有纳入事务管理,因此在springMVC的配置文件中排除了对Ser

struts2+spring3.2.9+hibernate4.2.0+atomikos3.8实现分布式事务JTA

目前开发的J2EE系统用到了两个数据源,需要分布式事物(JTA)的支持,但是tomcat不支持JTA,开发调试不太方便,本文通过使用atomikos实现了分布式事务的支持,理论可以运行在任何java容器中. 一.将以下jar包放到lib目录下 二.将配置文件jta.properties放到WEB-INF目录下,内容如下: 三.修改spring配置文件,建立2个数据源: <bean id="dataSource" class="com.atomikos.jdbc.Atom