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

你可能感兴趣的:(Hibernate)