标签:
本文通过一个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) );
代码分析:
本项目使用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;
}
}
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 }
处理事务的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 }
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 }
将项目打成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
使用spring+hibernate+atomikos+tomcat构建分布式事务
标签:
原文地址:http://www.cnblogs.com/rain-in-sun/p/4312120.html