一、开篇

这里整合分别采用了Hibernate和MyBatis两大持久层框架,Hibernate主要完成增删改功能和一些单一的对象查询功能,MyBatis主要负责查询功能。所以在出来数据库方言的时候基本上没有什么问题,但唯一可能出现问题的就是在hibernate做添加操作生成主键策略的时候。因为我们都知道hibernate的数据库本地方言会针对不同的数据库采用不同的主键生成策略。

所以针对这一问题不得不采用自定义的主键生成策略,自己写一个主键生成器的表来维护主键生成方式或以及使用其他的方式来生成主键,从而避开利用hibernate默认提供的主键生成方式。

所以存在问题有:怎样动态的切换数据库方言?

这个问题还没有解决,没有更多时间来研究。不过想想应该可以配置两个SessionFactory来实现,那又存在怎么样动态切换SessionFactory呢?!需要解决这个问题才行,而这里则演示怎么样动态切换DataSource数据源的方法。

 

二、代码演示

在演示开始之前你的项目已经成功的整合完成的情况下才行,如果你还不知道怎么使用Spring整合MyBatis和Spring整合Hibernate的话。建议参考之前的文章:MyBatis3整合Spring3、SpringMVC3Struts2、Spring、Hibernate整合ExtJS这两篇文章结合起来就可以完成整合是几大框架了。这里重点介绍动态切换DataSource数据源的方法!

1、datasource的配置 applicationContext-datasource.xml

  1. <?xml version="1.0" encoding="UTF-8"?>

  1. <beans xmlns="http://www.springframework.org/schema/beans"

  1. xmlns:aop="http://www.springframework.org/schema/aop"

  1. xmlns:tx="http://www.springframework.org/schema/tx"

  1. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

  1. xsi:schemaLocation="http://www.springframework.org/schema/beans

  1. http://www.springframework.org/schema/beans/spring-beans-3.2.xsd

  1. http://www.springframework.org/schema/aop

  1. http://www.springframework.org/schema/aop/spring-aop-3.2.xsd

  1. http://www.springframework.org/schema/tx

  1. http://www.springframework.org/schema/tx/spring-tx-3.2.xsd ">

  1. <!-- 配置c3p0数据源 -->

  1. <bean id="dataSourceOracle" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">

  1. <property name="driverClass" value="${datasource.driver}"/>

  1. <property name="jdbcUrl" value="${datasource.url}"/>

  1. <property name="user" value="${datasource.username}"/>

  1. <property name="password" value="${datasource.password}"/>

  1. <property name="acquireIncrement" value="${c3p0.acquireIncrement}"/>

  1. <property name="initialPoolSize" value="${c3p0.initialPoolSize}"/>

  1. <property name="minPoolSize" value="${c3p0.minPoolSize}"/>

  1. <property name="maxPoolSize" value="${c3p0.maxPoolSize}"/>

  1. <property name="maxIdleTime" value="${c3p0.maxIdleTime}"/>

  1. <property name="idleConnectionTestPeriod" value="${c3p0.idleConnectionTestPeriod}"/>

  1. <property name="maxStatements" value="${c3p0.maxStatements}"/>

  1. <property name="numHelperThreads" value="${c3p0.numHelperThreads}"/>

  1. <property name="preferredTestQuery" value="${c3p0.preferredTestQuery}"/>

  1. <property name="testConnectionOnCheckout" value="${c3p0.testConnectionOnCheckout}"/>

  1. </bean>

  1. <bean id="dataSourceMySQL" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">

  1. <property name="driverClass" value="com.mysql.jdbc.Driver"/>

  1. <property name="jdbcUrl" value="jdbc:mysql://172.31.108.178:3306/world?useUnicode=true&amp;characterEncoding=UTF-8&amp;zeroDateTimeBehavior=convertToNull"/>

  1. <property name="user" value="root"/>

  1. <property name="password" value="jp2011"/>

  1. <property name="acquireIncrement" value="${c3p0.acquireIncrement}"/>

  1. <property name="initialPoolSize" value="${c3p0.initialPoolSize}"/>

  1. <property name="minPoolSize" value="${c3p0.minPoolSize}"/>

  1. <property name="maxPoolSize" value="${c3p0.maxPoolSize}"/>

  1. <property name="maxIdleTime" value="${c3p0.maxIdleTime}"/>

  1. <property name="idleConnectionTestPeriod" value="${c3p0.idleConnectionTestPeriod}"/>

  1. <property name="maxStatements" value="${c3p0.maxStatements}"/>

  1. <property name="numHelperThreads" value="${c3p0.numHelperThreads}"/>

  1. <property name="preferredTestQuery" value="${c3p0.preferredTestQuery}"/>

  1. <property name="testConnectionOnCheckout" value="${c3p0.testConnectionOnCheckout}"/>

  1. </bean>

  1.  

  1. <bean id="multipleDataSource" class="com.hoo.framework.spring.support.MultipleDataSource">

  1. <property name="defaultTargetDataSource" ref="dataSourceOracle"/>

  1. <property name="targetDataSources">

  1. <map>

  1. <!-- 注意这里的value是和上面的DataSource的id对应,key要和下面的CustomerContextHolder中的常量对应 -->

  1. <entry value-ref="dataSourceOracle" key="oracleDataSource"/>

  1. <entry value-ref="dataSourceMySQL" key="mySqlDataSource"/>

  1. </map>

  1. </property>

  1. </bean>

  1. <!-- Annotation 配置sessionFactory,配置数据库连接,注入hibernate数据库配置 -->

  1. <bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">

  1. <property name="dataSource" ref="multipleDataSource"/>

  1. <property name="packagesToScan" value="com.hoo.**.entity"/>

  1. <property name="hibernateProperties">

  1. <props>

  1. <!-- prop key="hibernate.dialect">org.hibernate.dialect.OracleDialect</prop-->

  1. <!-- 链接释放策略 on_close | after_transaction | after_statement | auto -->

  1. <prop key="hibernate.connection.release_mode">after_transaction</prop>

  1. <prop key="hibernate.show_sql">true</prop>

  1. <prop key="hibernate.format_sql">true</prop>

  1. <!--prop key="hibernate.hbm2ddl.auto">update</prop-->

  1. </props>

  1. </property>

  1. <!-- property name="configLocation" value="classpath:hibernate.cfg.xml" /-->

  1. <property name="namingStrategy">

  1. <bean class="com.hoo.framework.hibernate.PrefixedNamingStrategy" />

  1. </property>

  1. </bean>

  1.  

  1. <!-- 事务管理器,注入sessionFactory -->

  1. <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">

  1. <property name="sessionFactory" ref="sessionFactory" />

  1. </bean>

  1. <!-- 配置事务的传播特性 -->

  1. <tx:advice id="txAdvice" transaction-manager="transactionManager">

  1. <tx:attributes>

  1. <tx:method name="add*" propagation="REQUIRED" rollback-for="java.lang.Exception"/>

  1. <tx:method name="edit*" propagation="REQUIRED" rollback-for="java.lang.Exception"/>

  1. <tx:method name="remove*" propagation="REQUIRED" rollback-for="java.lang.Exception"/>

  1. <tx:method name="insert*" propagation="REQUIRED" rollback-for="java.lang.Exception"/>

  1. <tx:method name="save*" propagation="REQUIRED" rollback-for="java.lang.Exception"/>

  1. <tx:method name="update*" propagation="REQUIRED" rollback-for="java.lang.Exception"/>

  1. <tx:method name="modify*" propagation="REQUIRED" rollback-for="java.lang.Exception"/>

  1. <tx:method name="delete*" propagation="REQUIRED" rollback-for="java.lang.Exception"/>

  1. <tx:method name="execute*" propagation="REQUIRED" rollback-for="java.lang.Exception"/>

  1. <tx:method name="*" read-only="true" />

  1. </tx:attributes>

  1. </tx:advice>

  1. <!-- 配置那些类、方法纳入到事务的管理 -->

  1. <aop:config>

  1. <aop:pointcut expression="execution(* com.hoo.**.service.impl.*.*(..))" id="transactionManagerMethod"/>

  1. <aop:advisor advice-ref="txAdvice" pointcut-ref="transactionManagerMethod" />

  1. </aop:config>

  1. <!-- 配置SqlSessionFactoryBean -->

  1. <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">

  1. <property name="dataSource" ref="multipleDataSource"/>

  1. <property name="configLocation" value="classpath:mybatis.xml"/>

  1. <!-- mapper和resultmap配置路径 -->

  1. <property name="mapperLocations">

  1. <list>

  1. <!-- 表示在com.hoo目录下的任意包下的resultmap包目录中,以-resultmap.xml或-mapper.xml结尾所有文件 -->

  1. <value>classpath:com/hoo/framework/mybatis/mybatis-common.xml</value>

  1. <value>classpath:com/hoo/**/resultmap/*-resultmap.xml</value>

  1. <value>classpath:com/hoo/**/mapper/*-mapper.xml</value>

  1. <value>classpath:com/hoo/**/mapper/**/*-mapper.xml</value>

  1. </list>

  1. </property>

  1. </bean>

  1. <!-- 通过扫描的模式,扫描目录在com/hoo/任意目录下的mapper目录下,所有的mapper都需要继承SqlMapper接口的接口 -->

  1. <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">

  1. <property name="basePackage" value="com.hoo.**.mapper"/>

  1. <property name="markerInterface" value="com.hoo.framework.mybatis.SqlMapper"/>

  1. </bean>

  1. </beans>

上面分配配置了Oracle和MySQL数据源,MultipleDataSource为自定义的DataSource,它是继承AbstractRoutingDataSource实现抽象方法即可。

 

2、MultipleDataSource实现AbstractRoutingDataSource抽象数据源中方法,定义CustomerContextHolder来动态切换数据源。代码如下:

  1. package com.hoo.framework.spring.support;

  1.  

  1. import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

  1.  

  1. /**

  1. * <b>function:</b> Spring 多数据源实现

  1. * @author hoojo

  1. * @createDate 2013-9-27 上午11:24:53

  1. * @file MultipleDataSource.java

  1. * @package com.hoo.framework.spring.support

  1. * @project SHMB

  1. * @blog http://blog.csdn.net/IBM_hoojo

  1. * @email hoojo_@126.com

  1. * @version 1.0

  1. */

  1. public class MultipleDataSource extends AbstractRoutingDataSource {

  1.  

  1. @Override

  1. protected Object determineCurrentLookupKey() {

  1. return CustomerContextHolder.getCustomerType();

  1. }

  1. }

 

CustomerContextHolder

  1. package com.hoo.framework.spring.support;

  1.  

  1. /**

  1. * <b>function:</b> 多数据源

  1. * @author hoojo

  1. * @createDate 2013-9-27 上午11:36:57

  1. * @file CustomerContextHolder.java

  1. * @package com.hoo.framework.spring.support

  1. * @project SHMB

  1. * @blog http://blog.csdn.net/IBM_hoojo

  1. * @email hoojo_@126.com

  1. * @version 1.0

  1. */

  1. public abstract class CustomerContextHolder {

  1.  

  1. public final static String DATA_SOURCE_ORACLE = "oracleDataSource";

  1. public final static String DATA_SOURCE_MYSQL = "mySqlDataSource";

  1. private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>();

  1. public static void setCustomerType(String customerType) {

  1. contextHolder.set(customerType);

  1. }

  1. public static String getCustomerType() {

  1. return contextHolder.get();

  1. }

  1. public static void clearCustomerType() {

  1. contextHolder.remove();

  1. }

  1. }

其中,常量对应的applicationContext-datasource.xml中的multipleDataSource中的targetDataSource的key,这个很关键不要搞错了。

 

3、测试看能否切换数据源

  1. package com.hoo.framework.service.impl;

  1.  

  1. import org.junit.Test;

  1. import org.junit.runner.RunWith;

  1. import org.springframework.beans.factory.annotation.Autowired;

  1. import org.springframework.beans.factory.annotation.Qualifier;

  1. import org.springframework.test.context.ContextConfiguration;

  1. import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

  1.  

  1. import com.hoo.framework.dao.BaseDao;

  1. import com.hoo.framework.log.ApplicationLogging;

  1. import com.hoo.framework.spring.support.CustomerContextHolder;

  1.  

  1.  

  1. /**

  1. * <b>function:</b>多数据源测试服务接口测试

  1. * @author hoojo

  1. * @createDate 2013-10-10 上午11:18:18

  1. * @file MultipleDataSourceServiceImplTest.java

  1. * @package com.hoo.framework.service.impl

  1. * @project SHMB

  1. * @blog http://blog.csdn.net/IBM_hoojo

  1. * @email hoojo_@126.com

  1. * @version 1.0

  1. */

  1. @ContextConfiguration({ "classpath:applicationContext-datasource.xml", "classpath:applicationContext-base.xml" })

  1. @RunWith(SpringJUnit4ClassRunner.class)

  1. public class MultipleDataSourceServiceImplTest extends ApplicationLogging {

  1.  

  1. @Autowired

  1. private BaseDao dao;

  1. @Test

  1. public void testDao() {

  1. info(dao.toString());

  1. CustomerContextHolder.setCustomerType(CustomerContextHolder.DATA_SOURCE_ORACLE);

  1. info(dao.findBySql("select * from devicestate_tab where rownum < 2").toString());

  1. CustomerContextHolder.setCustomerType(CustomerContextHolder.DATA_SOURCE_MYSQL);

  1. info(dao.findBySql("select * from city limit 2").toString());

  1. }

  1. }

运行上面的测试用例后可以发现能查询到数据,如果我们注释掉其中的一项setCustomerType就会出现查询错误。在其中一个数据库没有找到对应的table。

至此,切换数据源也算成功了大半,剩下的就是如何在实际的业务中完成数据源的“动态”切换呢?!难道还是要像上面一样在每个方法上面写一个setCustomerType来手动控制数据源!答案是“当然不是”。我们用过Spring、hibernate后就会知道,先去使用hibernate的时候没有用spring,事务都是手动控制的。自从用了Spring大家都轻松了很多,事务交给了Spring来完成。所以到了这里你大概知道怎么做了,如果你还不知道~嘿嘿……(Spring那你就懂了个皮毛,最经典的部分你没有学到)

所以就是利用Spring的Aop进行切面编程,拦截器Interceptor在这里是一个很好的选择。它可以在方法之前或方法之后完成一些操作,而且控制的粒度可以细到具体的方法中的参数、返回值、方法名等。在这里控制数据源动态切换最好不过了!

 

4、上面是手动切换数据源,我们用Spring的Aop拦截器整个更自动化的方法来切换数据源。

Spring配置文件 applicationContext-base.xml

  1. <?xml version="1.0" encoding="UTF-8"?>

  1. <beans xmlns="http://www.springframework.org/schema/beans"

  1. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

  1. xmlns:aop="http://www.springframework.org/schema/aop"

  1. xmlns:util="http://www.springframework.org/schema/util"

  1. xmlns:context="http://www.springframework.org/schema/context"

  1. xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd

  1. http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.2.xsd

  1. http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd

  1. http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd">

  1.  

  1. <context:component-scan base-package="com.hoo.**.dao.impl"/>

  1. <context:component-scan base-package="com.hoo.**.service.impl"/>

  1. <context:component-scan base-package="com.hoo.**.interceptor"/>

  1. <!-- 启用Aop AspectJ注解 -->

  1. <aop:aspectj-autoproxy/>

  1. <!-- 注入配置文件 -->

  1. <util:properties id="systemConfig" location="classpath:system.properties" />

  1. <!-- 启用表达式配置xml内容 -->

  1. <bean id="placeholderConfig" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">

  1. <property name="propertiesArray">

  1. <util:list>

  1. <util:properties location="classpath:system.properties"/>

  1. <util:properties location="classpath:datasource.properties"/>

  1. </util:list>

  1. </property>

  1. </bean>

  1. <!-- 配置一个拦截器对象,处理具体的切换数据源的业务 -->

  1. <bean id="dataSourceMethodInterceptor" class="com.hoo.framework.spring.interceptor.DataSourceMethodInterceptor"/>

  1. <!-- 参与动态切换数据源的切入点对象 (切入点对象,确定何时何地调用拦截器) -->

  1. <bean id="methodCachePointCut" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">

  1. <!-- 配置缓存aop切面 -->

  1. <property name="advice" ref="dataSourceMethodInterceptor" />

  1. <!-- 配置哪些方法参与缓存策略 -->

  1. <!--

  1. .表示符合任何单一字元

  1. ### +表示符合前一个字元一次或多次

  1. ### *表示符合前一个字元零次或多次

  1. ### \Escape任何Regular expression使用到的符号

  1. -->

  1. <!-- .*表示前面的前缀(包括包名) 表示print方法-->

  1. <property name="patterns">

  1. <list>

  1. <value>com.hoo.*.service.impl.*Service*\.*.*</value>

  1. <value>com.hoo.*.mapper.*Mapper*\.*.*</value>

  1. </list>

  1. </property>

  1. </bean>

  1. </beans>

上面的拦截器是拦截Service和Mapper的Java对象中的执行方法,所有在service.impl包下的ServiceImpl和mapper包下的Mapper接口将会被DataSourceMethodInterceptor拦截到,并通过其中的规律动态设置数据源。

 

3、拦截器DataSourceMethodInterceptor.java拦截具体的业务,并通过业务代码中的方法和接口、实现类的规律进行动态设置数据源

  1. package com.hoo.framework.spring.interceptor;

  1.  

  1. import java.lang.reflect.Proxy;

  1. import org.aopalliance.intercept.MethodInterceptor;

  1. import org.aopalliance.intercept.MethodInvocation;

  1. import org.apache.commons.lang.ClassUtils;

  1. import org.springframework.beans.factory.InitializingBean;

  1. import com.hoo.framework.log.ApplicationLogging;

  1. import com.hoo.framework.spring.support.CustomerContextHolder;

  1.  

  1. /**

  1. * <b>function:</b> 动态设置数据源拦截器

  1. * @author hoojo

  1. * @createDate 2013-9-27 下午02:00:13

  1. * @file DataSourceMethodInterceptor.java

  1. * @package com.hoo.framework.spring.interceptor

  1. * @project SHMB

  1. * @blog http://blog.csdn.net/IBM_hoojo

  1. * @email hoojo_@126.com

  1. * @version 1.0

  1. */

  1. public class DataSourceMethodInterceptor extends ApplicationLogging implements MethodInterceptor, InitializingBean {

  1.  

  1. @Override

  1. public Object invoke(MethodInvocation invocation) throws Throwable {

  1. Class<?> clazz = invocation.getThis().getClass();

  1. String className = clazz.getName();

  1. if (ClassUtils.isAssignable(clazz, Proxy.class)) {

  1. className = invocation.getMethod().getDeclaringClass().getName();

  1. }

  1. String methodName = invocation.getMethod().getName();

  1. Object[] arguments = invocation.getArguments();

  1. trace("execute {}.{}({})", className, methodName, arguments);

  1. if (className.contains("MySQL")) {

  1. CustomerContextHolder.setCustomerType(CustomerContextHolder.DATA_SOURCE_MYSQL);

  1. } else if (className.contains("Oracle")) {

  1. CustomerContextHolder.setCustomerType(CustomerContextHolder.DATA_SOURCE_ORACLE);

  1. } else if (methodName.contains("MySQL")) {

  1. CustomerContextHolder.setCustomerType(CustomerContextHolder.DATA_SOURCE_MYSQL);

  1. } else if (methodName.contains("Oracle")) {

  1. CustomerContextHolder.setCustomerType(CustomerContextHolder.DATA_SOURCE_ORACLE);

  1. } else {

  1. CustomerContextHolder.clearCustomerType();

  1. }

  1. /*

  1. if (className.contains("MySQL") || methodName.contains("MySQL")) {

  1. CustomerContextHolder.setCustomerType(CustomerContextHolder.DATA_SOURCE_MYSQL);

  1. } else if (className.contains("Oracle") || methodName.contains("Oracle")) {

  1. CustomerContextHolder.setCustomerType(CustomerContextHolder.DATA_SOURCE_ORACLE);

  1. } else {

  1. CustomerContextHolder.clearCustomerType();

  1. }

  1. */

  1. Object result = invocation.proceed();

  1. return result;

  1. }

  1.  

  1. @Override

  1. public void afterPropertiesSet() throws Exception {

  1. log.trace("afterPropertiesSet……");

  1. }

  1. }

上面的代码是在接口或实现中如果出现MySQL就设置数据源为DATA_SOURCE_MYSQL,如果有Oracle就切换成DATA_SOURCE_ORACLE数据源。

 

4、编写实际的业务接口和实现来测试拦截器是否有效

MultipleDataSourceService 接口

  1. package com.hoo.server.datasource.service;

  1.  

  1.  

  1. /**

  1. * <b>function:</b> 多数据源测试服务接口

  1. * @author hoojo

  1. * @createDate 2013-10-10 上午11:07:31

  1. * @file MultipleDataSourceService.java

  1. * @package com.hoo.server.datasource.service

  1. * @project SHMB

  1. * @blog http://blog.csdn.net/IBM_hoojo

  1. * @email hoojo_@126.com

  1. * @version 1.0

  1. */

  1. public interface MultipleDataSourceService {

  1. public void execute4MySQL() throws Exception;

  1. public void execute4Oracle() throws Exception;

  1. }

 

接口实现

  1. package com.hoo.server.datasource.service.impl;

  1.  

  1. import org.springframework.beans.factory.annotation.Autowired;

  1. import org.springframework.stereotype.Service;

  1.  

  1. import com.hoo.framework.dao.BaseDao;

  1. import com.hoo.framework.service.impl.AbstractService;

  1. import com.hoo.server.datasource.service.MultipleDataSourceService;

  1.  

  1. /**

  1. * <b>function:</b> 多数据源测试服务接口实现

  1. * @author hoojo

  1. * @createDate 2013-10-10 上午11:09:54

  1. * @file MultipleDataSourceServiceImpl.java

  1. * @package com.hoo.server.datasource.service.impl

  1. * @project SHMB

  1. * @blog http://blog.csdn.net/IBM_hoojo

  1. * @email hoojo_@126.com

  1. * @version 1.0

  1. */

  1. @Service

  1. public class MultipleDataSourceServiceImpl extends AbstractService implements MultipleDataSourceService {

  1. @Autowired

  1. private BaseDao dao;

  1. @Override

  1. public void execute4MySQL() throws Exception {

  1. info(dao.findBySql("select * from city limit 2").toString());

  1. }

  1.  

  1. @Override

  1. public void execute4Oracle() throws Exception {

  1. info(dao.findBySql("select * from devicestate_tab where rownum < 2").toString());

  1. }

  1. }

测试上面的服务层代码,看看能否利用拦截器实现数据源动态切换

在上面的MultipleDataSourceServiceImplTest中加入如下代码

  1. @Autowired

  1. @Qualifier("multipleDataSourceServiceImpl")

  1. private MultipleDataSourceService service;

  1.  

  1. @Test

  1. public void testService() {

  1. try {

  1. service.execute4MySQL();

  1. service.execute4Oracle();

  1. } catch (Exception e) {

  1. e.printStackTrace();

  1. }

  1. }

运行上面的代码后可以看到能够成功查询到结果

 

5、测试实现类带Oracle或MySQL字符串的

  1. package com.hoo.server.datasource.service.impl;

  1.  

  1. import org.springframework.beans.factory.annotation.Autowired;

  1. import org.springframework.stereotype.Service;

  1. import com.hoo.framework.dao.BaseDao;

  1. import com.hoo.framework.service.impl.AbstractService;

  1. import com.hoo.server.datasource.service.MultipleDataSourceService;

  1.  

  1. /**

  1. * <b>function:</b> 多数据源测试服务接口实现

  1. * @author hoojo

  1. * @createDate 2013-10-10 上午11:09:54

  1. * @file MultipleDataSourceServiceImpl.java

  1. * @package com.hoo.server.datasource.service.impl

  1. * @project SHMB

  1. * @blog http://blog.csdn.net/IBM_hoojo

  1. * @email hoojo_@126.com

  1. * @version 1.0

  1. */

  1. @Service

  1. public class MySQLDataSourceServiceImpl extends AbstractService implements MultipleDataSourceService {

  1. @Autowired

  1. private BaseDao dao;

  1. @Override

  1. public void execute4MySQL() throws Exception {

  1. info(dao.findBySql("select * from city limit 2").toString());

  1. }

  1.  

  1. @Override

  1. public void execute4Oracle() throws Exception {

  1. info(dao.findBySql("select * from devicestate_tab where rownum < 2").toString());

  1. }

  1. }

 

  1. package com.hoo.server.datasource.service.impl;

  1.  

  1. import org.springframework.beans.factory.annotation.Autowired;

  1. import org.springframework.stereotype.Service;

  1. import com.hoo.framework.dao.BaseDao;

  1. import com.hoo.framework.service.impl.AbstractService;

  1. import com.hoo.server.datasource.service.MultipleDataSourceService;

  1.  

  1. /**

  1. * <b>function:</b> 多数据源测试服务接口实现

  1. * @author hoojo

  1. * @createDate 2013-10-10 上午11:09:54

  1. * @file MultipleDataSourceServiceImpl.java

  1. * @package com.hoo.server.datasource.service.impl

  1. * @project SHMB

  1. * @blog http://blog.csdn.net/IBM_hoojo

  1. * @email hoojo_@126.com

  1. * @version 1.0

  1. */

  1. @Service

  1. public class OracleDataSourceServiceImpl extends AbstractService implements MultipleDataSourceService {

  1. @Autowired

  1. private BaseDao dao;

  1. @Override

  1. public void execute4MySQL() throws Exception {

  1. info(dao.findBySql("select * from city limit 2").toString());

  1. }

  1.  

  1. @Override

  1. public void execute4Oracle() throws Exception {

  1. info(dao.findBySql("select * from devicestate_tab where rownum < 2").toString());

  1. }

  1. }

这里的两个实现类的类名都含有不同规则的数据源标识符字符串,而且方法名也含有相关字符串,这些都匹配拦截器中的规则。

在MultipleDataSourceServiceImplTest 中加入测试代码

  1. @Autowired

  1. @Qualifier("oracleDataSourceServiceImpl")

  1. private MultipleDataSourceService oracleService;

  1.  

  1. @Autowired

  1. @Qualifier("mySQLDataSourceServiceImpl")

  1. private MultipleDataSourceService mySQLService;

  1.  

  1. @Test

  1. public void testOracleService() {

  1. try {

  1. oracleService.execute4MySQL();

  1. } catch (Exception e1) {

  1. e1.printStackTrace();

  1. }

  1. try {

  1. oracleService.execute4Oracle();

  1. } catch (Exception e) {

  1. e.printStackTrace();

  1. }

  1. }

  1.  

  1. @Test

  1. public void testMySQLService() {

  1. try {

  1. mySQLService.execute4MySQL();

  1. } catch (Exception e1) {

  1. e1.printStackTrace();

  1. }

  1. try {

  1. mySQLService.execute4Oracle();

  1. } catch (Exception e) {

  1. e.printStackTrace();

  1. }

  1. }

执行上面的测试用例会发现有一个查询会失败,那是因为我们按照拦截器中的业务规则切换数据源就匹配到了其中一个,就是通过类名进行数据源切换,所以只定位到其中一个数据源。

 

6、测试MyBatis的数据源切换方法

MyBatis的查询接口

  1. package com.hoo.server.datasource.mapper;

  1.  

  1. import java.util.List;

  1. import java.util.Map;

  1.  

  1. import com.hoo.framework.mybatis.SqlMapper;

  1.  

  1. /**

  1. * <b>function:</b> MyBatis 多数据源 测试查询接口

  1. * @author hoojo

  1. * @createDate 2013-10-10 下午04:18:08

  1. * @file MultipleDataSourceMapper.java

  1. * @package com.hoo.server.datasource.mapper

  1. * @project SHMB

  1. * @blog http://blog.csdn.net/IBM_hoojo

  1. * @email hoojo_@126.com

  1. * @version 1.0

  1. */

  1. public interface MultipleDataSourceMapper extends SqlMapper {

  1.  

  1. public List<Map<String, Object>> execute4MySQL() throws Exception;

  1. public List<Map<String, Object>> execute4Oracle() throws Exception;

  1. }

multiple-datasource-mapper.xml

  1. <?xml version="1.0" encoding="UTF-8" ?>

  1. <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

  1. <mapper namespace="com.hoo.server.datasource.mapper.MultipleDataSourceMapper">

  1. <select id="execute4Oracle" resultType="map">

  1. <![CDATA[

  1. SELECT

  1. *

  1. FROM

  1. deviceInfo_tab t where rownum < 10

  1. ]]>

  1. </select>

  1. <select id="execute4MySQL" resultType="map">

  1. <![CDATA[

  1. SELECT

  1. *

  1. FROM

  1. city limit 2

  1. ]]>

  1. </select>

  1. </mapper>

测试MyBatis的mapper查询接口,在MultipleDataSourceServiceImplTest加入以下代码

  1. @Autowired

  1. private MultipleDataSourceMapper mapper;

  1.  

  1. @Test

  1. public void testMapper() {

  1. try {

  1. trace(mapper.execute4MySQL());

  1. } catch (Exception e1) {

  1. e1.printStackTrace();

  1. }

  1. try {

  1. trace(mapper.execute4Oracle());

  1. } catch (Exception e) {

  1. e.printStackTrace();

  1. }

  1. }

运行以上测试代码也能发现可以正常的查询到Oracle和MySQL数据库中的数据。MyBatis的在这里只负责查询,而增删改是hibernate完成的任务,所以这里也就不再测试modified部分。

 

7、上面的拦截器是需要在配置文件中进行配置的,这里利用annotation的配置的拦截器进行业务拦截,也许有些人更喜欢用annotation

  1. package com.hoo.framework.spring.interceptor;

  1.  

  1. import java.lang.reflect.Proxy;

  1. import org.apache.commons.lang.ClassUtils;

  1. import org.aspectj.lang.JoinPoint;

  1. import org.aspectj.lang.annotation.Aspect;

  1. import org.aspectj.lang.annotation.Before;

  1. import org.springframework.stereotype.Component;

  1. import com.hoo.framework.log.ApplicationLogging;

  1. import com.hoo.framework.spring.support.CustomerContextHolder;

  1.  

  1. /**

  1. * <b>function:</b> 多数据源动态配置拦截器

  1. * @author hoojo

  1. * @createDate 2013-10-10 上午11:35:54

  1. * @file MultipleDataSourceInterceptor.java

  1. * @package com.hoo.framework.spring.interceptor

  1. * @project SHMB

  1. * @blog http://blog.csdn.net/IBM_hoojo

  1. * @email hoojo_@126.com

  1. * @version 1.0

  1. */

  1. @Component

  1. @Aspect

  1. public class MultipleDataSourceInterceptor extends ApplicationLogging {

  1.  

  1. /**

  1. * <b>function:</b> 动态设置数据源

  1. * @author hoojo

  1. * @createDate 2013-10-10 上午11:38:45

  1. * @throws Exception

  1. */

  1. @Before("execution(* com.hoo..service.impl.*ServiceImpl.*(..)) || execution(* com.hoo..mapper.*Mapper.*(..))")

  1. public void dynamicSetDataSoruce(JoinPoint joinPoint) throws Exception {

  1. Class<?> clazz = joinPoint.getTarget().getClass();

  1. String className = clazz.getName();

  1. if (ClassUtils.isAssignable(clazz, Proxy.class)) {

  1. className = joinPoint.getSignature().getDeclaringTypeName();

  1. }

  1. String methodName = joinPoint.getSignature().getName();

  1. Object[] arguments = joinPoint.getArgs();

  1. trace("execute {}.{}({})", className, methodName, arguments);

  1. if (className.contains("MySQL")) {

  1. CustomerContextHolder.setCustomerType(CustomerContextHolder.DATA_SOURCE_MYSQL);

  1. } else if (className.contains("Oracle")) {

  1. CustomerContextHolder.setCustomerType(CustomerContextHolder.DATA_SOURCE_ORACLE);

  1. } else if (methodName.contains("MySQL")) {

  1. CustomerContextHolder.setCustomerType(CustomerContextHolder.DATA_SOURCE_MYSQL);

  1. } else if (methodName.contains("Oracle")) {

  1. CustomerContextHolder.setCustomerType(CustomerContextHolder.DATA_SOURCE_ORACLE);

  1. } else {

  1. CustomerContextHolder.clearCustomerType();

  1. }

  1. /*

  1. if (className.contains("MySQL") || methodName.contains("MySQL")) {

  1. CustomerContextHolder.setCustomerType(CustomerContextHolder.DATA_SOURCE_MYSQL);

  1. } else if (className.contains("Oracle") || methodName.contains("Oracle")) {

  1. CustomerContextHolder.setCustomerType(CustomerContextHolder.DATA_SOURCE_ORACLE);

  1. } else {

  1. CustomerContextHolder.clearCustomerType();

  1. }

  1. */

  1. }

  1. }

这种拦截器就是不需要在配置文件中加入任何配置进行拦截,算是一种扩展的方法。

 

三、总结

多数据源动态切换的主要地方在于我们要定义一个自己的数据源来实现AbstractRoutingDataSource中的determineCurrentLookupKey方法,然后通过CustomerContextHolder来实现数据源的切换工作。而数据源的动态切换也就在于我们利用了Spring的Aop中的拦截器Interceptor进行业务类的方法进行拦截,通过类名或方法名中的有效字符串来动态切换到我们定义好的规则对应的数据源。

Spring3.3 整合 Hibernate3、MyBatis3.2 配置多数据源/动态切换数据源 方法的更多相关文章

  1. Spring3.3 整合 Hibernate3、MyBatis3.2 配置多数据源/动态切换数据源方法

    一.开篇 这里整合分别采用了Hibernate和MyBatis两大持久层框架,Hibernate主要完成增删改功能和一些单一的对象查询功能,MyBatis主要负责查询功能.所以在出来数据库方言的时候基 ...

  2. springmvc+mybatis多数据源配置,AOP注解动态切换数据源

    springmvc与springboot没多大区别,springboot一个jar包配置几乎包含了所有springmvc,也不需要繁琐的xml配置,springmvc需要配置多种jar包,需要繁琐的x ...

  3. spring+myBatis 配置多数据源,切换数据源

    注:本文来源于  tianzhiwuqis <spring+myBatis 配置多数据源,切换数据源> 一个项目里一般情况下只会使用到一个数据库,但有的需求是要显示其他数据库的内容,像这样 ...

  4. Springboot多数据源配置--数据源动态切换

    在上一篇我们介绍了多数据源,但是我们会发现在实际中我们很少直接获取数据源对象进行操作,我们常用的是jdbcTemplate或者是jpa进行操作数据库.那么这一节我们将要介绍怎么进行多数据源动态切换.添 ...

  5. Spring Boot数据访问之多数据源配置及数据源动态切换

    如果一个数据库数据量过大,考虑到分库分表和读写分离需要动态的切换到相应的数据库进行相关操作,这样就会有多个数据源.对于一个数据源的配置在Spring Boot数据访问之数据源自动配置 - 池塘里洗澡的 ...

  6. springboot整合druid连接池、mybatis实现多数据源动态切换

    demo环境: JDK 1.8 ,Spring boot 1.5.14 一 整合durid 1.添加druid连接池maven依赖 <dependency> <groupId> ...

  7. [转]SpringMVC+ Mybatis 配置多数据源 + 手动切换数据源

    正确可行的解决方法:使用Spring提供的AbstractRoutingDataSource类来根据请求路由到不同的数据源.具体做法是先设置两个不同的dataSource代表不同的数据源,再建一个总的 ...

  8. spring-boot整合mybaits多数据源动态切换案例

    1.运行环境 开发工具:intellij idea JDK版本:1.8 项目管理工具:Maven 4.0.0 2.GITHUB地址 https://github.com/nbfujx/springBo ...

  9. Spring3 整合MyBatis3 配置多数据源 动态选择SqlSessionFactory

    一.摘要 上两篇文章分别介绍了Spring3.3 整合 Hibernate3.MyBatis3.2 配置多数据源/动态切换数据源 方法 和 Spring3 整合Hibernate3.5 动态切换Ses ...

随机推荐

  1. UICollectionView高级实践

    上一篇中的干货看完,不觉感觉还是有点虚,今天我们来点实的,做了两个小DEMO,源码已放GitHub,主要是针对UICollectionView做了联系.第一个DEMO是瀑布流的一个练习:第二个DEMO ...

  2. 20145225 《Java程序设计》第2周学习总结

    20145225<Java程序设计> 第2周学习总结 教材学习内容总结 3.1.1Java的类型 分为基本类型(Primitive type)和类类型(Class type) 基本类型: ...

  3. hdu 4107

    Gangster Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Su ...

  4. cach

    为程序使用内存缓存(MemoryCache) oscache Guava cache 一种解决方法是配一个listener,在里面启动定时器. 简单缓存可以封装LinkedHashMap,因为它是有顺 ...

  5. Windows 8.1 应用再出发 (WinJS) - 几种新增控件(1)

    Windows 8.1 和 WinJS 引入了以下新控件和功能,分别是:AppBarCommand.BackButton.Hub.ItemContainer.NavBar.Repeater.WebVi ...

  6. [ACM] poj 1064 Cable master (二分查找)

    Cable master Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 21071   Accepted: 4542 Des ...

  7. 热烈庆祝杨学明老师为苏宁、中兴、烽火、CNNIC、创维、金立、中航信等知名企业提供培训和咨询服务!

    在2015年三季度,研发资深顾问.资深讲师杨学明先生为国内多家名企提供了培训和咨询服务!由于杨学明老师在软件及互联网方面的管理经验极为丰富,被多家公司选为首席研发讲师!并聘为常年顾问!

  8. Unity3d动画脚本 Animation Scripting(深入了解游戏引擎中的动画处理原理)

    也许这一篇文章的内容有点枯燥,但我要说的是如果你想深入的了解游戏引擎是如何处理动画片断或者素材并 让玩家操控的角色动起来栩栩如生,那么这真是一篇好文章(当然我仅仅是翻译了一下)   动画脚本 Anim ...

  9. centos 安装PGSQL

    centos 安装PGSQLCentOS下yum安装PostgreSQL目录 1 Configure YUM repository2 Install PGDG RPM file3 Install Po ...

  10. Windows2003 架设VPN服务

    一.确保Windows防火墙关闭. 在我的电脑上右键=>管理,在左边窗口找到“服务和应用程序”,展开,单击“服务”,在右边窗口中找到“Windows Firewall/Internet Conn ...