一、开篇

这里整合分别采用了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"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:aop="http://www.springframework.org/schema/aop"
  4. xmlns:tx="http://www.springframework.org/schema/tx"
  5. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  6. xsi:schemaLocation="http://www.springframework.org/schema/beans
  7. http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
  8. http://www.springframework.org/schema/aop
  9. http://www.springframework.org/schema/aop/spring-aop-3.2.xsd
  10. http://www.springframework.org/schema/tx
  11. http://www.springframework.org/schema/tx/spring-tx-3.2.xsd ">
  12.  
  13. <!-- 配置c3p0数据源 -->
  14. <bean id="dataSourceOracle" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
  15. <property name="driverClass" value="${datasource.driver}"/>
  16. <property name="jdbcUrl" value="${datasource.url}"/>
  17. <property name="user" value="${datasource.username}"/>
  18. <property name="password" value="${datasource.password}"/>
  19.  
  20. <property name="acquireIncrement" value="${c3p0.acquireIncrement}"/>
  21. <property name="initialPoolSize" value="${c3p0.initialPoolSize}"/>
  22. <property name="minPoolSize" value="${c3p0.minPoolSize}"/>
  23. <property name="maxPoolSize" value="${c3p0.maxPoolSize}"/>
  24. <property name="maxIdleTime" value="${c3p0.maxIdleTime}"/>
  25. <property name="idleConnectionTestPeriod" value="${c3p0.idleConnectionTestPeriod}"/>
  26. <property name="maxStatements" value="${c3p0.maxStatements}"/>
  27. <property name="numHelperThreads" value="${c3p0.numHelperThreads}"/>
  28. <property name="preferredTestQuery" value="${c3p0.preferredTestQuery}"/>
  29. <property name="testConnectionOnCheckout" value="${c3p0.testConnectionOnCheckout}"/>
  30. </bean>
  31.  
  32. <bean id="dataSourceMySQL" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
  33. <property name="driverClass" value="com.mysql.jdbc.Driver"/>
  34. <property name="jdbcUrl" value="jdbc:mysql://172.31.108.178:3306/world?useUnicode=true&amp;characterEncoding=UTF-8&amp;zeroDateTimeBehavior=convertToNull"/>
  35. <property name="user" value="root"/>
  36. <property name="password" value="jp2011"/>
  37.  
  38. <property name="acquireIncrement" value="${c3p0.acquireIncrement}"/>
  39. <property name="initialPoolSize" value="${c3p0.initialPoolSize}"/>
  40. <property name="minPoolSize" value="${c3p0.minPoolSize}"/>
  41. <property name="maxPoolSize" value="${c3p0.maxPoolSize}"/>
  42. <property name="maxIdleTime" value="${c3p0.maxIdleTime}"/>
  43. <property name="idleConnectionTestPeriod" value="${c3p0.idleConnectionTestPeriod}"/>
  44. <property name="maxStatements" value="${c3p0.maxStatements}"/>
  45. <property name="numHelperThreads" value="${c3p0.numHelperThreads}"/>
  46. <property name="preferredTestQuery" value="${c3p0.preferredTestQuery}"/>
  47. <property name="testConnectionOnCheckout" value="${c3p0.testConnectionOnCheckout}"/>
  48. </bean>
  49.  
  50. <bean id="multipleDataSource" class="com.hoo.framework.spring.support.MultipleDataSource">
  51. <property name="defaultTargetDataSource" ref="dataSourceOracle"/>
  52. <property name="targetDataSources">
  53. <map>
  54. <!-- 注意这里的value是和上面的DataSource的id对应,key要和下面的CustomerContextHolder中的常量对应 -->
  55. <entry value-ref="dataSourceOracle" key="oracleDataSource"/>
  56. <entry value-ref="dataSourceMySQL" key="mySqlDataSource"/>
  57. </map>
  58. </property>
  59. </bean>
  60.  
  61. <!-- Annotation 配置sessionFactory,配置数据库连接,注入hibernate数据库配置 -->
  62. <bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
  63. <property name="dataSource" ref="multipleDataSource"/>
  64. <property name="packagesToScan" value="com.hoo.**.entity"/>
  65. <property name="hibernateProperties">
  66. <props>
  67. <!-- prop key="hibernate.dialect">org.hibernate.dialect.OracleDialect</prop-->
  68. <!-- 链接释放策略 on_close | after_transaction | after_statement | auto -->
  69. <prop key="hibernate.connection.release_mode">after_transaction</prop>
  70. <prop key="hibernate.show_sql">true</prop>
  71. <prop key="hibernate.format_sql">true</prop>
  72. <!--prop key="hibernate.hbm2ddl.auto">update</prop-->
  73. </props>
  74. </property>
  75. <!-- property name="configLocation" value="classpath:hibernate.cfg.xml" /-->
  76. <property name="namingStrategy">
  77. <bean class="com.hoo.framework.hibernate.PrefixedNamingStrategy" />
  78. </property>
  79. </bean>
  80.  
  81. <!-- 事务管理器,注入sessionFactory -->
  82. <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
  83. <property name="sessionFactory" ref="sessionFactory" />
  84. </bean>
  85.  
  86. <!-- 配置事务的传播特性 -->
  87. <tx:advice id="txAdvice" transaction-manager="transactionManager">
  88. <tx:attributes>
  89. <tx:method name="add*" propagation="REQUIRED" rollback-for="java.lang.Exception"/>
  90. <tx:method name="edit*" propagation="REQUIRED" rollback-for="java.lang.Exception"/>
  91. <tx:method name="remove*" propagation="REQUIRED" rollback-for="java.lang.Exception"/>
  92. <tx:method name="insert*" propagation="REQUIRED" rollback-for="java.lang.Exception"/>
  93. <tx:method name="save*" propagation="REQUIRED" rollback-for="java.lang.Exception"/>
  94. <tx:method name="update*" propagation="REQUIRED" rollback-for="java.lang.Exception"/>
  95. <tx:method name="modify*" propagation="REQUIRED" rollback-for="java.lang.Exception"/>
  96. <tx:method name="delete*" propagation="REQUIRED" rollback-for="java.lang.Exception"/>
  97. <tx:method name="execute*" propagation="REQUIRED" rollback-for="java.lang.Exception"/>
  98. <tx:method name="*" read-only="true" />
  99. </tx:attributes>
  100. </tx:advice>
  101.  
  102. <!-- 配置那些类、方法纳入到事务的管理 -->
  103. <aop:config>
  104. <aop:pointcut expression="execution(* com.hoo.**.service.impl.*.*(..))" id="transactionManagerMethod"/>
  105. <aop:advisor advice-ref="txAdvice" pointcut-ref="transactionManagerMethod" />
  106. </aop:config>
  107.  
  108. <!-- 配置SqlSessionFactoryBean -->
  109. <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
  110. <property name="dataSource" ref="multipleDataSource"/>
  111. <property name="configLocation" value="classpath:mybatis.xml"/>
  112. <!-- mapper和resultmap配置路径 -->
  113. <property name="mapperLocations">
  114. <list>
  115. <!-- 表示在com.hoo目录下的任意包下的resultmap包目录中,以-resultmap.xml或-mapper.xml结尾所有文件 -->
  116. <value>classpath:com/hoo/framework/mybatis/mybatis-common.xml</value>
  117. <value>classpath:com/hoo/**/resultmap/*-resultmap.xml</value>
  118. <value>classpath:com/hoo/**/mapper/*-mapper.xml</value>
  119. <value>classpath:com/hoo/**/mapper/**/*-mapper.xml</value>
  120. </list>
  121. </property>
  122. </bean>
  123.  
  124. <!-- 通过扫描的模式,扫描目录在com/hoo/任意目录下的mapper目录下,所有的mapper都需要继承SqlMapper接口的接口 -->
  125. <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
  126. <property name="basePackage" value="com.hoo.**.mapper"/>
  127. <property name="markerInterface" value="com.hoo.framework.mybatis.SqlMapper"/>
  128. </bean>
  129. </beans>

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

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

  1. package com.hoo.framework.spring.support;
  2.  
  3. import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
  4.  
  5. /**
  6. * <b>function:</b> Spring 多数据源实现
  7. * @author hoojo
  8. * @createDate 2013-9-27 上午11:24:53
  9. * @file MultipleDataSource.java
  10. * @package com.hoo.framework.spring.support
  11. * @project SHMB
  12. * @blog http://blog.csdn.net/IBM_hoojo
  13. * @email hoojo_@126.com
  14. * @version 1.0
  15. */
  16. public class MultipleDataSource extends AbstractRoutingDataSource {
  17.  
  18. @Override
  19. protected Object determineCurrentLookupKey() {
  20. return CustomerContextHolder.getCustomerType();
  21. }
  22. }

CustomerContextHolder

  1. package com.hoo.framework.spring.support;
  2.  
  3. /**
  4. * <b>function:</b> 多数据源
  5. * @author hoojo
  6. * @createDate 2013-9-27 上午11:36:57
  7. * @file CustomerContextHolder.java
  8. * @package com.hoo.framework.spring.support
  9. * @project SHMB
  10. * @blog http://blog.csdn.net/IBM_hoojo
  11. * @email hoojo_@126.com
  12. * @version 1.0
  13. */
  14. public abstract class CustomerContextHolder {
  15.  
  16. public final static String DATA_SOURCE_ORACLE = "oracleDataSource";
  17. public final static String DATA_SOURCE_MYSQL = "mySqlDataSource";
  18.  
  19. private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>();
  20.  
  21. public static void setCustomerType(String customerType) {
  22. contextHolder.set(customerType);
  23. }
  24.  
  25. public static String getCustomerType() {
  26. return contextHolder.get();
  27. }
  28.  
  29. public static void clearCustomerType() {
  30. contextHolder.remove();
  31. }
  32. }

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

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

  1. package com.hoo.framework.service.impl;
  2.  
  3. import org.junit.Test;
  4. import org.junit.runner.RunWith;
  5. import org.springframework.beans.factory.annotation.Autowired;
  6. import org.springframework.beans.factory.annotation.Qualifier;
  7. import org.springframework.test.context.ContextConfiguration;
  8. import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
  9.  
  10. import com.hoo.framework.dao.BaseDao;
  11. import com.hoo.framework.log.ApplicationLogging;
  12. import com.hoo.framework.spring.support.CustomerContextHolder;
  13.  
  14. /**
  15. * <b>function:</b>多数据源测试服务接口测试
  16. * @author hoojo
  17. * @createDate 2013-10-10 上午11:18:18
  18. * @file MultipleDataSourceServiceImplTest.java
  19. * @package com.hoo.framework.service.impl
  20. * @project SHMB
  21. * @blog http://blog.csdn.net/IBM_hoojo
  22. * @email hoojo_@126.com
  23. * @version 1.0
  24. */
  25. @ContextConfiguration({ "classpath:applicationContext-datasource.xml", "classpath:applicationContext-base.xml" })
  26. @RunWith(SpringJUnit4ClassRunner.class)
  27. public class MultipleDataSourceServiceImplTest extends ApplicationLogging {
  28.  
  29. @Autowired
  30. private BaseDao dao;
  31.  
  32. @Test
  33. public void testDao() {
  34. info(dao.toString());
  35.  
  36. CustomerContextHolder.setCustomerType(CustomerContextHolder.DATA_SOURCE_ORACLE);
  37. info(dao.findBySql("select * from devicestate_tab where rownum < 2").toString());
  38.  
  39. CustomerContextHolder.setCustomerType(CustomerContextHolder.DATA_SOURCE_MYSQL);
  40. info(dao.findBySql("select * from city limit 2").toString());
  41. }
  42. }

运行上面的测试用例后可以发现能查询到数据,如果我们注释掉其中的一项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"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xmlns:aop="http://www.springframework.org/schema/aop"
  5. xmlns:util="http://www.springframework.org/schema/util"
  6. xmlns:context="http://www.springframework.org/schema/context"
  7. xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
  8. http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.2.xsd
  9. http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd
  10. http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd">
  11.  
  12. <context:component-scan base-package="com.hoo.**.dao.impl"/>
  13. <context:component-scan base-package="com.hoo.**.service.impl"/>
  14. <context:component-scan base-package="com.hoo.**.interceptor"/>
  15.  
  16. <!-- 启用Aop AspectJ注解 -->
  17. <aop:aspectj-autoproxy/>
  18.  
  19. <!-- 注入配置文件 -->
  20. <util:properties id="systemConfig" location="classpath:system.properties" />
  21.  
  22. <!-- 启用表达式配置xml内容 -->
  23. <bean id="placeholderConfig" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
  24. <property name="propertiesArray">
  25. <util:list>
  26. <util:properties location="classpath:system.properties"/>
  27. <util:properties location="classpath:datasource.properties"/>
  28. </util:list>
  29. </property>
  30. </bean>
  31.  
  32. <!-- 配置一个拦截器对象,处理具体的切换数据源的业务 -->
  33. <bean id="dataSourceMethodInterceptor" class="com.hoo.framework.spring.interceptor.DataSourceMethodInterceptor"/>
  34.  
  35. <!-- 参与动态切换数据源的切入点对象 (切入点对象,确定何时何地调用拦截器) -->
  36. <bean id="methodCachePointCut" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
  37. <!-- 配置缓存aop切面 -->
  38. <property name="advice" ref="dataSourceMethodInterceptor" />
  39. <!-- 配置哪些方法参与缓存策略 -->
  40. <!--
  41. .表示符合任何单一字元
  42. ### +表示符合前一个字元一次或多次
  43. ### *表示符合前一个字元零次或多次
  44. ### \Escape任何Regular expression使用到的符号
  45. -->
  46. <!-- .*表示前面的前缀(包括包名) 表示print方法-->
  47. <property name="patterns">
  48. <list>
  49. <value>com.hoo.*.service.impl.*Service*\.*.*</value>
  50. <value>com.hoo.*.mapper.*Mapper*\.*.*</value>
  51. </list>
  52. </property>
  53. </bean>
  54. </beans>

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

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

  1. package com.hoo.framework.spring.interceptor;
  2.  
  3. import java.lang.reflect.Proxy;
  4. import org.aopalliance.intercept.MethodInterceptor;
  5. import org.aopalliance.intercept.MethodInvocation;
  6. import org.apache.commons.lang.ClassUtils;
  7. import org.springframework.beans.factory.InitializingBean;
  8. import com.hoo.framework.log.ApplicationLogging;
  9. import com.hoo.framework.spring.support.CustomerContextHolder;
  10.  
  11. /**
  12. * <b>function:</b> 动态设置数据源拦截器
  13. * @author hoojo
  14. * @createDate 2013-9-27 下午02:00:13
  15. * @file DataSourceMethodInterceptor.java
  16. * @package com.hoo.framework.spring.interceptor
  17. * @project SHMB
  18. * @blog http://blog.csdn.net/IBM_hoojo
  19. * @email hoojo_@126.com
  20. * @version 1.0
  21. */
  22. public class DataSourceMethodInterceptor extends ApplicationLogging implements MethodInterceptor, InitializingBean {
  23.  
  24. @Override
  25. public Object invoke(MethodInvocation invocation) throws Throwable {
  26. Class<?> clazz = invocation.getThis().getClass();
  27. String className = clazz.getName();
  28. if (ClassUtils.isAssignable(clazz, Proxy.class)) {
  29. className = invocation.getMethod().getDeclaringClass().getName();
  30. }
  31. String methodName = invocation.getMethod().getName();
  32. Object[] arguments = invocation.getArguments();
  33. trace("execute {}.{}({})", className, methodName, arguments);
  34.  
  35. if (className.contains("MySQL")) {
  36. CustomerContextHolder.setCustomerType(CustomerContextHolder.DATA_SOURCE_MYSQL);
  37. } else if (className.contains("Oracle")) {
  38. CustomerContextHolder.setCustomerType(CustomerContextHolder.DATA_SOURCE_ORACLE);
  39. } else if (methodName.contains("MySQL")) {
  40. CustomerContextHolder.setCustomerType(CustomerContextHolder.DATA_SOURCE_MYSQL);
  41. } else if (methodName.contains("Oracle")) {
  42. CustomerContextHolder.setCustomerType(CustomerContextHolder.DATA_SOURCE_ORACLE);
  43. } else {
  44. CustomerContextHolder.clearCustomerType();
  45. }
  46.  
  47. /*
  48. if (className.contains("MySQL") || methodName.contains("MySQL")) {
  49. CustomerContextHolder.setCustomerType(CustomerContextHolder.DATA_SOURCE_MYSQL);
  50. } else if (className.contains("Oracle") || methodName.contains("Oracle")) {
  51. CustomerContextHolder.setCustomerType(CustomerContextHolder.DATA_SOURCE_ORACLE);
  52. } else {
  53. CustomerContextHolder.clearCustomerType();
  54. }
  55. */
  56. Object result = invocation.proceed();
  57. return result;
  58. }
  59.  
  60. @Override
  61. public void afterPropertiesSet() throws Exception {
  62. log.trace("afterPropertiesSet……");
  63. }
  64. }

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

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

MultipleDataSourceService 接口

  1. package com.hoo.server.datasource.service;
  2.  
  3. /**
  4. * <b>function:</b> 多数据源测试服务接口
  5. * @author hoojo
  6. * @createDate 2013-10-10 上午11:07:31
  7. * @file MultipleDataSourceService.java
  8. * @package com.hoo.server.datasource.service
  9. * @project SHMB
  10. * @blog http://blog.csdn.net/IBM_hoojo
  11. * @email hoojo_@126.com
  12. * @version 1.0
  13. */
  14. public interface MultipleDataSourceService {
  15.  
  16. public void execute4MySQL() throws Exception;
  17.  
  18. public void execute4Oracle() throws Exception;
  19. }

接口实现

  1. package com.hoo.server.datasource.service.impl;
  2.  
  3. import org.springframework.beans.factory.annotation.Autowired;
  4. import org.springframework.stereotype.Service;
  5.  
  6. import com.hoo.framework.dao.BaseDao;
  7. import com.hoo.framework.service.impl.AbstractService;
  8. import com.hoo.server.datasource.service.MultipleDataSourceService;
  9.  
  10. /**
  11. * <b>function:</b> 多数据源测试服务接口实现
  12. * @author hoojo
  13. * @createDate 2013-10-10 上午11:09:54
  14. * @file MultipleDataSourceServiceImpl.java
  15. * @package com.hoo.server.datasource.service.impl
  16. * @project SHMB
  17. * @blog http://blog.csdn.net/IBM_hoojo
  18. * @email hoojo_@126.com
  19. * @version 1.0
  20. */
  21. @Service
  22. public class MultipleDataSourceServiceImpl extends AbstractService implements MultipleDataSourceService {
  23.  
  24. @Autowired
  25. private BaseDao dao;
  26.  
  27. @Override
  28. public void execute4MySQL() throws Exception {
  29. info(dao.findBySql("select * from city limit 2").toString());
  30. }
  31.  
  32. @Override
  33. public void execute4Oracle() throws Exception {
  34. info(dao.findBySql("select * from devicestate_tab where rownum < 2").toString());
  35. }
  36. }

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

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

  1. @Autowired
  2. @Qualifier("multipleDataSourceServiceImpl")
  3. private MultipleDataSourceService service;
  4.  
  5. @Test
  6. public void testService() {
  7. try {
  8. service.execute4MySQL();
  9. service.execute4Oracle();
  10. } catch (Exception e) {
  11. e.printStackTrace();
  12. }
  13. }

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

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

  1. package com.hoo.server.datasource.service.impl;
  2.  
  3. import org.springframework.beans.factory.annotation.Autowired;
  4. import org.springframework.stereotype.Service;
  5. import com.hoo.framework.dao.BaseDao;
  6. import com.hoo.framework.service.impl.AbstractService;
  7. import com.hoo.server.datasource.service.MultipleDataSourceService;
  8.  
  9. /**
  10. * <b>function:</b> 多数据源测试服务接口实现
  11. * @author hoojo
  12. * @createDate 2013-10-10 上午11:09:54
  13. * @file MultipleDataSourceServiceImpl.java
  14. * @package com.hoo.server.datasource.service.impl
  15. * @project SHMB
  16. * @blog http://blog.csdn.net/IBM_hoojo
  17. * @email hoojo_@126.com
  18. * @version 1.0
  19. */
  20. @Service
  21. public class MySQLDataSourceServiceImpl extends AbstractService implements MultipleDataSourceService {
  22.  
  23. @Autowired
  24. private BaseDao dao;
  25.  
  26. @Override
  27. public void execute4MySQL() throws Exception {
  28. info(dao.findBySql("select * from city limit 2").toString());
  29. }
  30.  
  31. @Override
  32. public void execute4Oracle() throws Exception {
  33. info(dao.findBySql("select * from devicestate_tab where rownum < 2").toString());
  34. }
  35. }
  1. package com.hoo.server.datasource.service.impl;
  2.  
  3. import org.springframework.beans.factory.annotation.Autowired;
  4. import org.springframework.stereotype.Service;
  5. import com.hoo.framework.dao.BaseDao;
  6. import com.hoo.framework.service.impl.AbstractService;
  7. import com.hoo.server.datasource.service.MultipleDataSourceService;
  8.  
  9. /**
  10. * <b>function:</b> 多数据源测试服务接口实现
  11. * @author hoojo
  12. * @createDate 2013-10-10 上午11:09:54
  13. * @file MultipleDataSourceServiceImpl.java
  14. * @package com.hoo.server.datasource.service.impl
  15. * @project SHMB
  16. * @blog http://blog.csdn.net/IBM_hoojo
  17. * @email hoojo_@126.com
  18. * @version 1.0
  19. */
  20. @Service
  21. public class OracleDataSourceServiceImpl extends AbstractService implements MultipleDataSourceService {
  22.  
  23. @Autowired
  24. private BaseDao dao;
  25.  
  26. @Override
  27. public void execute4MySQL() throws Exception {
  28. info(dao.findBySql("select * from city limit 2").toString());
  29. }
  30.  
  31. @Override
  32. public void execute4Oracle() throws Exception {
  33. info(dao.findBySql("select * from devicestate_tab where rownum < 2").toString());
  34. }
  35. }

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

在MultipleDataSourceServiceImplTest 中加入测试代码

  1. @Autowired
  2. @Qualifier("oracleDataSourceServiceImpl")
  3. private MultipleDataSourceService oracleService;
  4.  
  5. @Autowired
  6. @Qualifier("mySQLDataSourceServiceImpl")
  7. private MultipleDataSourceService mySQLService;
  8.  
  9. @Test
  10. public void testOracleService() {
  11. try {
  12. oracleService.execute4MySQL();
  13. } catch (Exception e1) {
  14. e1.printStackTrace();
  15. }
  16. try {
  17. oracleService.execute4Oracle();
  18. } catch (Exception e) {
  19. e.printStackTrace();
  20. }
  21. }
  22.  
  23. @Test
  24. public void testMySQLService() {
  25. try {
  26. mySQLService.execute4MySQL();
  27. } catch (Exception e1) {
  28. e1.printStackTrace();
  29. }
  30. try {
  31. mySQLService.execute4Oracle();
  32. } catch (Exception e) {
  33. e.printStackTrace();
  34. }
  35. }

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

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

MyBatis的查询接口

  1. package com.hoo.server.datasource.mapper;
  2.  
  3. import java.util.List;
  4. import java.util.Map;
  5.  
  6. import com.hoo.framework.mybatis.SqlMapper;
  7.  
  8. /**
  9. * <b>function:</b> MyBatis 多数据源 测试查询接口
  10. * @author hoojo
  11. * @createDate 2013-10-10 下午04:18:08
  12. * @file MultipleDataSourceMapper.java
  13. * @package com.hoo.server.datasource.mapper
  14. * @project SHMB
  15. * @blog http://blog.csdn.net/IBM_hoojo
  16. * @email hoojo_@126.com
  17. * @version 1.0
  18. */
  19. public interface MultipleDataSourceMapper extends SqlMapper {
  20.  
  21. public List<Map<String, Object>> execute4MySQL() throws Exception;
  22.  
  23. public List<Map<String, Object>> execute4Oracle() throws Exception;
  24. }

multiple-datasource-mapper.xml

  1. <?xml version="1.0" encoding="UTF-8" ?>
  2. <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
  3. <mapper namespace="com.hoo.server.datasource.mapper.MultipleDataSourceMapper">
  4.  
  5. <select id="execute4Oracle" resultType="map">
  6. <![CDATA[
  7. SELECT
  8. *
  9. FROM
  10. deviceInfo_tab t where rownum < 10
  11. ]]>
  12. </select>
  13.  
  14. <select id="execute4MySQL" resultType="map">
  15. <![CDATA[
  16. SELECT
  17. *
  18. FROM
  19. city limit 2
  20. ]]>
  21. </select>
  22. </mapper>

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

  1. @Autowired
  2. private MultipleDataSourceMapper mapper;
  3.  
  4. @Test
  5. public void testMapper() {
  6. try {
  7. trace(mapper.execute4MySQL());
  8. } catch (Exception e1) {
  9. e1.printStackTrace();
  10. }
  11. try {
  12. trace(mapper.execute4Oracle());
  13. } catch (Exception e) {
  14. e.printStackTrace();
  15. }
  16. }

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

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

  1. package com.hoo.framework.spring.interceptor;
  2.  
  3. import java.lang.reflect.Proxy;
  4. import org.apache.commons.lang.ClassUtils;
  5. import org.aspectj.lang.JoinPoint;
  6. import org.aspectj.lang.annotation.Aspect;
  7. import org.aspectj.lang.annotation.Before;
  8. import org.springframework.stereotype.Component;
  9. import com.hoo.framework.log.ApplicationLogging;
  10. import com.hoo.framework.spring.support.CustomerContextHolder;
  11.  
  12. /**
  13. * <b>function:</b> 多数据源动态配置拦截器
  14. * @author hoojo
  15. * @createDate 2013-10-10 上午11:35:54
  16. * @file MultipleDataSourceInterceptor.java
  17. * @package com.hoo.framework.spring.interceptor
  18. * @project SHMB
  19. * @blog http://blog.csdn.net/IBM_hoojo
  20. * @email hoojo_@126.com
  21. * @version 1.0
  22. */
  23. @Component
  24. @Aspect
  25. public class MultipleDataSourceInterceptor extends ApplicationLogging {
  26.  
  27. /**
  28. * <b>function:</b> 动态设置数据源
  29. * @author hoojo
  30. * @createDate 2013-10-10 上午11:38:45
  31. * @throws Exception
  32. */
  33. @Before("execution(* com.hoo..service.impl.*ServiceImpl.*(..)) || execution(* com.hoo..mapper.*Mapper.*(..))")
  34. public void dynamicSetDataSoruce(JoinPoint joinPoint) throws Exception {
  35.  
  36. Class<?> clazz = joinPoint.getTarget().getClass();
  37. String className = clazz.getName();
  38. if (ClassUtils.isAssignable(clazz, Proxy.class)) {
  39. className = joinPoint.getSignature().getDeclaringTypeName();
  40. }
  41. String methodName = joinPoint.getSignature().getName();
  42. Object[] arguments = joinPoint.getArgs();
  43. trace("execute {}.{}({})", className, methodName, arguments);
  44.  
  45. if (className.contains("MySQL")) {
  46. CustomerContextHolder.setCustomerType(CustomerContextHolder.DATA_SOURCE_MYSQL);
  47. } else if (className.contains("Oracle")) {
  48. CustomerContextHolder.setCustomerType(CustomerContextHolder.DATA_SOURCE_ORACLE);
  49. } else if (methodName.contains("MySQL")) {
  50. CustomerContextHolder.setCustomerType(CustomerContextHolder.DATA_SOURCE_MYSQL);
  51. } else if (methodName.contains("Oracle")) {
  52. CustomerContextHolder.setCustomerType(CustomerContextHolder.DATA_SOURCE_ORACLE);
  53. } else {
  54. CustomerContextHolder.clearCustomerType();
  55. }
  56.  
  57. /*
  58. if (className.contains("MySQL") || methodName.contains("MySQL")) {
  59. CustomerContextHolder.setCustomerType(CustomerContextHolder.DATA_SOURCE_MYSQL);
  60. } else if (className.contains("Oracle") || methodName.contains("Oracle")) {
  61. CustomerContextHolder.setCustomerType(CustomerContextHolder.DATA_SOURCE_ORACLE);
  62. } else {
  63. CustomerContextHolder.clearCustomerType();
  64. }
  65. */
  66. }
  67. }

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

三、总结

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

本文转自:http://www.cnblogs.com/hoojo/p/Spring_Hibernate_MyBatis_MultipleDataSource_switchDataSource.html

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. hibernater-validator jar包冲突的问题

    在引用hibernater-validator jar包时一直抛出异常,在引用带有该包的项目,或者同时在一个项目中使用该包和validator包都会抛出以下异常 最后发现是在Eclipse环境下,不能 ...

  2. ElasticSearchserver操作命令

    在win7环境,进入elasticsearch安装文件夹的bin文件夹: 1. elasticsearch.bat 就能够启动elasticsearch了.执行这个插件的优点是:elasticsear ...

  3. 虚拟网卡TUN/TAP 驱动程序设计原理

    昨天韦哥写了<Linux下Tun/Tap设备通信原理>一文,只提到了两个使用Tun的用户进程之间的通信路径,并没有说明Tun虚拟网卡驱动是如何实现的,而正好看到了这里的一篇讲解这方面的文章 ...

  4. 换站点Logo图片---轻开电子商务系统(企业入门级B2C站点)

    一共2个文件: 显示及上传文件:site/links/img_logo.html 保存图片文件:site/links/img_logo_up1.chtml 在轻开电子商务系统(企业入门级B2C站点)的 ...

  5. tween用户使用指南

    tween.js user guide tween.js用户指南 1.What is a tween? How do they work? Why do you want to use them? 一 ...

  6. “checkbox”和“select”对象在javascript和jquery的操作差异做了整理

    checkbox checkbox在javascript和jquery中选中和取消的方法 Javascript: document.getElementById("myCheck" ...

  7. iOS8 Push Notifications

    本文转载至 http://blog.csdn.net/pjk1129/article/details/39551887   原贴地址:https://parse.com/tutorials/ios-p ...

  8. Redisson实现Redis分布式锁的N种姿势(转)

    Redis几种架构 Redis发展到现在,几种常见的部署架构有: 单机模式: 主从模式: 哨兵模式: 集群模式: 我们首先基于这些架构讲解Redisson普通分布式锁实现,需要注意的是,只有充分了解普 ...

  9. tornado安全应用之用户认证

    在这个例子中,我们将只通过存储在安全cookie里的用户名标识一个人.当某人首次在某个浏览器(或cookie过期后)访问我们的页面时,我们展示一个登录表单页面.表单作为到LoginHandler路由的 ...

  10. jps不显示java进程信息

    本来想自己整理,发现已经有前人整理,并且完美解决了我的问题,故转载,感谢分享 转自:http://trinea.iteye.com/blog/1196400 对于jps较熟悉可以直接查看第二部分的分析 ...