本文通过一个demo,介绍如何使用spring+hibernate管理多个数据源,注意,本文的事务管理并非之前博文介绍的分布式事务。
这个demo将使用两个事务管理器分别管理两个数据源。对于每一个独立的事务,只涉及一个数据源。
demo功能:实现一个能依靠两个独立的事务管理器互不干涉的管理自己的数据源的web demo。
demo将实现:
1.独立地控制两个不同的数据源的事务管理器。
测试方式:restful web api
使用工具:
spring 4.1.1.RELEASE
hibernate 4.2.4.Final
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 sql
代码分析:
本项目使用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 <bean id="qadataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" 17 destroy-method="close"> 18 <property name="jdbcUrl" value="${qa.db.url}"></property> 19 <property name="driverClass" value="com.mysql.jdbc.Driver"></property> 20 <property name="user" value="${qa.db.user}"></property> 21 <property name="password" value="${qa.db.password}"></property> 22 <property name="maxPoolSize" value="20"></property> 23 <property name="minPoolSize" value="20"></property> 24 <property name="initialPoolSize" value="20"></property> 25 <property name="maxIdleTime" value="200"></property> 26 <!--<property name="numHelperThreads" value="50"></property>--> 27 </bean> 28 29 <!--<bean id="qadataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">--> 30 <!--<property name="driverClassName" value="com.mysql.jdbc.Driver"/>--> 31 <!--<property name="url" value="${qa.db.url}"/>--> 32 <!--<property name="username" value="${qa.db.user}"/>--> 33 <!--<property name="password" value="${qa.db.password}"/>--> 34 <!--</bean>--> 35 36 <!--<bean id="devdataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">--> 37 <!--<property name="driverClassName" value="com.mysql.jdbc.Driver"/>--> 38 <!--<property name="url" value="${dev.db.url}"/>--> 39 <!--<property name="username" value="${dev.db.user}"/>--> 40 <!--<property name="password" value="${dev.db.password}"/>--> 41 <!--</bean>--> 42 43 <bean id="devdataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" 44 destroy-method="close"> 45 <property name="jdbcUrl" value="${dev.db.url}"></property> 46 <property name="driverClass" value="com.mysql.jdbc.Driver"></property> 47 <property name="user" value="${dev.db.user}"></property> 48 <property name="password" value="${dev.db.password}"></property> 49 <property name="maxPoolSize" value="20"></property> 50 <property name="minPoolSize" value="20"></property> 51 <property name="initialPoolSize" value="20"></property> 52 <property name="maxIdleTime" value="200"></property> 53 <!--<property name="numHelperThreads" value="50"></property>--> 54 </bean> 55 56 57 <bean id="qaSessionFactory" 58 class="org.springframework.orm.hibernate4.LocalSessionFactoryBean"> 59 <property name="packagesToScan" value="com.xy.model"/> 60 <property name="dataSource" ref="qadataSource"/> 61 <property name="hibernateProperties"> 62 <props> 63 <prop key="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</prop> 64 <prop key="hibernate.show_sql">false</prop> 65 <prop key="hibernate.autoReconnect">true</prop> 66 <prop key="hibernate.connection.release_mode">after_transaction</prop> 67 </props> 68 </property> 69 </bean> 70 71 <bean id="devSessionFactory" 72 class="org.springframework.orm.hibernate4.LocalSessionFactoryBean"> 73 <property name="packagesToScan" value="com.xy.model"/> 74 <property name="dataSource" ref="devdataSource"/> 75 <property name="hibernateProperties"> 76 <props> 77 <prop key="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</prop> 78 <prop key="hibernate.show_sql">false</prop> 79 <prop key="hibernate.autoReconnect">true</prop> 80 <prop key="hibernate.connection.release_mode">after_transaction</prop> 81 </props> 82 </property> 83 </bean> 84 85 <bean id="qaManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager"> 86 <property name="sessionFactory" ref="qaSessionFactory" /> 87 </bean> 88 89 <bean id="devManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager"> 90 <property name="sessionFactory" ref="devSessionFactory" /> 91 </bean> 92 93 </beans>
bean配置
其中qadataSource和devdataSource是对应两个数据库的数据源,qaSessionFactory和devSessionFactory是hibernate的sessionfactory。qaManager与devManage分别管理qadataSource和devdataSource的事务,互不干涉。注意事务管理器必须是hibernate4的类型。
Model类如下:package com.xy.model
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 = "nameDev") 11 public class NameDev { 12 @Id 13 @GeneratedValue(strategy = GenerationType.AUTO) 14 private long id; 15 private String nameDev; 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 getNameDev() { 26 return nameDev; 27 } 28 29 public void setNameDev(String nameDev) { 30 this.nameDev = nameDev; 31 } 32 }
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(value = "qaManager",rollbackFor = Exception.class) 21 public void addQa(boolean hasQaException) throws Exception { 22 NameQa nameQa = new NameQa(); 23 nameQa.setNameQa("hello"); 24 qaSessionFactory.getCurrentSession().save(nameQa); 25 26 if(hasQaException){ 27 throw new Exception(); 28 } 29 } 30 31 @Transactional(value = "devManager", rollbackFor = Exception.class) 32 public void addDev(boolean hasDevQaException) throws Exception { 33 NameDev nameDev = new NameDev(); 34 nameDev.setNameDev("hello"); 35 devSessionFactory.getCurrentSession().save(nameDev); 36 37 if(hasDevQaException){ 38 throw new Exception(); 39 } 40 } 41 42 }
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("hasQaException") boolean hasQaException, 23 @RequestParam("hasDevException") boolean hasDevException){ 24 try { 25 nameService.addQa(hasQaException); 26 } catch (Exception e) { 27 e.printStackTrace(); 28 } 29 30 try { 31 nameService.addDev(hasDevException); 32 } catch (Exception e) { 33 e.printStackTrace(); 34 } 35 return new ModelMap("true"); 36 } 37 38 39 }
controller
将项目打成war包,命名为test.war部署在tomcat上。
测试:
1.POST http://localhost:8080/test/addName.json
request parameters:
withDevException=false
witQaException=false
返回:true 两个数据都添加成功
2.POST http://localhost:8080/test/addName.json
request parameters:
withDevException=false
witQaException=true
返回:true 只有dev添加成功
源码下载:http://files.cnblogs.com/files/rain-in-sun/springmvc-hibernate-multidatasource.rar