1、開篇html
這裏整合分別採用了Hibernate和MyBatis兩大持久層框架,Hibernate主要完成增刪改功能和一些單一的對象查詢功能,MyBatis主要負責查詢功能。因此在出來數據庫方言的時候基本上沒有什麼問題,但惟一可能出現問題的就是在hibernate作添加操做生成主鍵策略的時候。由於咱們都知道hibernate的數據庫本地方言會針對不一樣的數據庫採用不一樣的主鍵生成策略。java
因此針對這一問題不得不採用自定義的主鍵生成策略,本身寫一個主鍵生成器的表來維護主鍵生成方式或以及使用其餘的方式來生成主鍵,從而避開利用hibernate默認提供的主鍵生成方式。mysql
因此存在問題有:怎樣動態的切換數據庫方言?spring
這個問題尚未解決,沒有更多時間來研究。不過想一想應該能夠配置兩個SessionFactory來實現,那又存在怎麼樣動態切換SessionFactory呢?!須要解決這個問題才行,而這裏則演示怎麼樣動態切換DataSource數據源的方法。sql
2、代碼演示數據庫
在演示開始以前你的項目已經成功的整合完成的狀況下才行,若是你還不知道怎麼使用Spring整合MyBatis和Spring整合Hibernate的話。建議參考以前的文章:MyBatis3整合Spring三、SpringMVC3、Struts二、Spring、Hibernate整合ExtJS這兩篇文章結合起來就能夠完成整合是幾大框架了。這裏重點介紹動態切換DataSource數據源的方法!express
一、datasource的配置 applicationContext-datasource.xmlapache
<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"xmlns:aop="http://www.springframework.org/schema/aop"xmlns:tx="http://www.springframework.org/schema/tx"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans-3.2.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop-3.2.xsdhttp://www.springframework.org/schema/txhttp://www.springframework.org/schema/tx/spring-tx-3.2.xsd "><!-- 配置c3p0數據源 --><bean id="dataSourceOracle" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close"><property name="driverClass" value="${datasource.driver}"/><property name="jdbcUrl" value="${datasource.url}"/><property name="user" value="${datasource.username}"/><property name="password" value="${datasource.password}"/><property name="acquireIncrement" value="${c3p0.acquireIncrement}"/><property name="initialPoolSize" value="${c3p0.initialPoolSize}"/><property name="minPoolSize" value="${c3p0.minPoolSize}"/><property name="maxPoolSize" value="${c3p0.maxPoolSize}"/><property name="maxIdleTime" value="${c3p0.maxIdleTime}"/><property name="idleConnectionTestPeriod" value="${c3p0.idleConnectionTestPeriod}"/><property name="maxStatements" value="${c3p0.maxStatements}"/><property name="numHelperThreads" value="${c3p0.numHelperThreads}"/><property name="preferredTestQuery" value="${c3p0.preferredTestQuery}"/><property name="testConnectionOnCheckout" value="${c3p0.testConnectionOnCheckout}"/></bean><bean id="dataSourceMySQL" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close"><property name="driverClass" value="com.mysql.jdbc.Driver"/><property name="jdbcUrl" value="jdbc:mysql://172.31.108.178:3306/world?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull"/><property name="user" value="root"/><property name="password" value="jp2011"/><property name="acquireIncrement" value="${c3p0.acquireIncrement}"/><property name="initialPoolSize" value="${c3p0.initialPoolSize}"/><property name="minPoolSize" value="${c3p0.minPoolSize}"/><property name="maxPoolSize" value="${c3p0.maxPoolSize}"/><property name="maxIdleTime" value="${c3p0.maxIdleTime}"/><property name="idleConnectionTestPeriod" value="${c3p0.idleConnectionTestPeriod}"/><property name="maxStatements" value="${c3p0.maxStatements}"/><property name="numHelperThreads" value="${c3p0.numHelperThreads}"/><property name="preferredTestQuery" value="${c3p0.preferredTestQuery}"/><property name="testConnectionOnCheckout" value="${c3p0.testConnectionOnCheckout}"/></bean><bean id="multipleDataSource" class="com.hoo.framework.spring.support.MultipleDataSource"><property name="defaultTargetDataSource" ref="dataSourceOracle"/><property name="targetDataSources"><map><!-- 注意這裏的value是和上面的DataSource的id對應,key要和下面的CustomerContextHolder中的常量對應 --><entry value-ref="dataSourceOracle" key="oracleDataSource"/><entry value-ref="dataSourceMySQL" key="mySqlDataSource"/></map></property></bean><!-- Annotation 配置sessionFactory,配置數據庫鏈接,注入hibernate數據庫配置 --><bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean"><property name="dataSource" ref="multipleDataSource"/><property name="packagesToScan" value="com.hoo.**.entity"/><property name="hibernateProperties"><props><!-- prop key="hibernate.dialect">org.hibernate.dialect.OracleDialect</prop--><!-- 連接釋放策略 on_close | after_transaction | after_statement | auto --><prop key="hibernate.connection.release_mode">after_transaction</prop><prop key="hibernate.show_sql">true</prop><prop key="hibernate.format_sql">true</prop><!--prop key="hibernate.hbm2ddl.auto">update</prop--></props></property><!-- property name="configLocation" value="classpath:hibernate.cfg.xml" /--><property name="namingStrategy"><bean class="com.hoo.framework.hibernate.PrefixedNamingStrategy" /></property></bean><!-- 事務管理器,注入sessionFactory --><bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"><property name="sessionFactory" ref="sessionFactory" /></bean><!-- 配置事務的傳播特性 --><tx:advice id="txAdvice" transaction-manager="transactionManager"><tx:attributes><tx:method name="add*" propagation="REQUIRED" rollback-for="java.lang.Exception"/><tx:method name="edit*" propagation="REQUIRED" rollback-for="java.lang.Exception"/><tx:method name="remove*" propagation="REQUIRED" rollback-for="java.lang.Exception"/><tx:method name="insert*" propagation="REQUIRED" rollback-for="java.lang.Exception"/><tx:method name="save*" propagation="REQUIRED" rollback-for="java.lang.Exception"/><tx:method name="update*" propagation="REQUIRED" rollback-for="java.lang.Exception"/><tx:method name="modify*" propagation="REQUIRED" rollback-for="java.lang.Exception"/><tx:method name="delete*" propagation="REQUIRED" rollback-for="java.lang.Exception"/><tx:method name="execute*" propagation="REQUIRED" rollback-for="java.lang.Exception"/><tx:method name="*" read-only="true" /></tx:attributes></tx:advice><!-- 配置那些類、方法歸入到事務的管理 --><aop:config><aop:pointcut expression="execution(* com.hoo.**.service.impl.*.*(..))" id="transactionManagerMethod"/><aop:advisor advice-ref="txAdvice" pointcut-ref="transactionManagerMethod" /></aop:config><!-- 配置SqlSessionFactoryBean --><bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"><property name="dataSource" ref="multipleDataSource"/><property name="configLocation" value="classpath:mybatis.xml"/><!-- mapper和resultmap配置路徑 --><property name="mapperLocations"><list><!-- 表示在com.hoo目錄下的任意包下的resultmap包目錄中,以-resultmap.xml或-mapper.xml結尾全部文件 --><value>classpath:com/hoo/framework/mybatis/mybatis-common.xml</value><value>classpath:com/hoo/**/resultmap/*-resultmap.xml</value><value>classpath:com/hoo/**/mapper/*-mapper.xml</value><value>classpath:com/hoo/**/mapper/**/*-mapper.xml</value></list></property></bean><!-- 經過掃描的模式,掃描目錄在com/hoo/任意目錄下的mapper目錄下,全部的mapper都須要繼承SqlMapper接口的接口 --><bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"><property name="basePackage" value="com.hoo.**.mapper"/><property name="markerInterface" value="com.hoo.framework.mybatis.SqlMapper"/></bean></beans>
上面分配配置了Oracle和MySQL數據源,MultipleDataSource爲自定義的DataSource,它是繼承AbstractRoutingDataSource實現抽象方法便可。編程
二、MultipleDataSource實現AbstractRoutingDataSource抽象數據源中方法,定義CustomerContextHolder來動態切換數據源。代碼以下:緩存
package com.hoo.framework.spring.support;import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;/*** <b>function:</b> Spring 多數據源實現* @author hoojo* @createDate 2013-9-27 上午11:24:53* @file MultipleDataSource.java* @package com.hoo.framework.spring.support* @project SHMB* @blog http://blog.csdn.net/IBM_hoojo* @email hoojo_@126.com* @version 1.0*/public class MultipleDataSource extends AbstractRoutingDataSource {@Overrideprotected Object determineCurrentLookupKey() {return CustomerContextHolder.getCustomerType();}}
CustomerContextHolder
package com.hoo.framework.spring.support;/*** <b>function:</b> 多數據源* @author hoojo* @createDate 2013-9-27 上午11:36:57* @file CustomerContextHolder.java* @package com.hoo.framework.spring.support* @project SHMB* @blog http://blog.csdn.net/IBM_hoojo* @email hoojo_@126.com* @version 1.0*/public abstract class CustomerContextHolder {public final static String DATA_SOURCE_ORACLE = "oracleDataSource";public final static String DATA_SOURCE_MYSQL = "mySqlDataSource";private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>();public static void setCustomerType(String customerType) {contextHolder.set(customerType);}public static String getCustomerType() {return contextHolder.get();}public static void clearCustomerType() {contextHolder.remove();}}
其中,常量對應的applicationContext-datasource.xml中的multipleDataSource中的targetDataSource的key,這個很關鍵不要搞錯了。
三、測試看可否切換數據源
package com.hoo.framework.service.impl;import org.junit.Test;import org.junit.runner.RunWith;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.beans.factory.annotation.Qualifier;import org.springframework.test.context.ContextConfiguration;import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;import com.hoo.framework.dao.BaseDao;import com.hoo.framework.log.ApplicationLogging;import com.hoo.framework.spring.support.CustomerContextHolder;/*** <b>function:</b>多數據源測試服務接口測試* @author hoojo* @createDate 2013-10-10 上午11:18:18* @file MultipleDataSourceServiceImplTest.java* @package com.hoo.framework.service.impl* @project SHMB* @blog http://blog.csdn.net/IBM_hoojo* @email hoojo_@126.com* @version 1.0*/@ContextConfiguration({ "classpath:applicationContext-datasource.xml", "classpath:applicationContext-base.xml" })@RunWith(SpringJUnit4ClassRunner.class)public class MultipleDataSourceServiceImplTest extends ApplicationLogging {@Autowiredprivate BaseDao dao;@Testpublic void testDao() {info(dao.toString());CustomerContextHolder.setCustomerType(CustomerContextHolder.DATA_SOURCE_ORACLE);info(dao.findBySql("select * from devicestate_tab where rownum < 2").toString());CustomerContextHolder.setCustomerType(CustomerContextHolder.DATA_SOURCE_MYSQL);info(dao.findBySql("select * from city limit 2").toString());}}
運行上面的測試用例後能夠發現能查詢到數據,若是咱們註釋掉其中的一項setCustomerType就會出現查詢錯誤。在其中一個數據庫沒有找到對應的table。
至此,切換數據源也算成功了大半,剩下的就是如何在實際的業務中完成數據源的「動態」切換呢?!難道仍是要像上面同樣在每一個方法上面寫一個setCustomerType來手動控制數據源!答案是「固然不是」。咱們用過Spring、hibernate後就會知道,先去使用hibernate的時候沒有用spring,事務都是手動控制的。自從用了Spring你們都輕鬆了不少,事務交給了Spring來完成。因此到了這裏你大概知道怎麼作了,若是你還不知道~嘿嘿……(Spring那你就懂了個皮毛,最經典的部分你沒有學到)
因此就是利用Spring的Aop進行切面編程,攔截器Interceptor在這裏是一個很好的選擇。它能夠在方法以前或方法以後完成一些操做,並且控制的粒度能夠細到具體的方法中的參數、返回值、方法名等。在這裏控制數據源動態切換最好不過了!
四、上面是手動切換數據源,咱們用Spring的Aop攔截器整個更自動化的方法來切換數據源。
Spring配置文件 applicationContext-base.xml
<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:aop="http://www.springframework.org/schema/aop"xmlns:util="http://www.springframework.org/schema/util"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsdhttp://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.2.xsdhttp://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsdhttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd"><context:component-scan base-package="com.hoo.**.dao.impl"/><context:component-scan base-package="com.hoo.**.service.impl"/><context:component-scan base-package="com.hoo.**.interceptor"/><!-- 啓用Aop AspectJ註解 --><aop:aspectj-autoproxy/><!-- 注入配置文件 --><util:properties id="systemConfig" location="classpath:system.properties" /><!-- 啓用表達式配置xml內容 --><bean id="placeholderConfig" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"><property name="propertiesArray"><util:list><util:properties location="classpath:system.properties"/><util:properties location="classpath:datasource.properties"/></util:list></property></bean><!-- 配置一個攔截器對象,處理具體的切換數據源的業務 --><bean id="dataSourceMethodInterceptor" class="com.hoo.framework.spring.interceptor.DataSourceMethodInterceptor"/><!-- 參與動態切換數據源的切入點對象 (切入點對象,肯定什麼時候何地調用攔截器) --><bean id="methodCachePointCut" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"><!-- 配置緩存aop切面 --><property name="advice" ref="dataSourceMethodInterceptor" /><!-- 配置哪些方法參與緩存策略 --><!--.表示符合任何單一字元### +表示符合前一個字元一次或屢次### *表示符合前一個字元零次或屢次### \Escape任何Regular expression使用到的符號--><!-- .*表示前面的前綴(包括包名) 表示print方法--><property name="patterns"><list><value>com.hoo.*.service.impl.*Service*\.*.*</value><value>com.hoo.*.mapper.*Mapper*\.*.*</value></list></property></bean></beans>
上面的攔截器是攔截Service和Mapper的Java對象中的執行方法,全部在service.impl包下的ServiceImpl和mapper包下的Mapper接口將會被DataSourceMethodInterceptor攔截到,並經過其中的規律動態設置數據源。
三、攔截器DataSourceMethodInterceptor.java攔截具體的業務,並經過業務代碼中的方法和接口、實現類的規律進行動態設置數據源
package com.hoo.framework.spring.interceptor;import java.lang.reflect.Proxy;import org.aopalliance.intercept.MethodInterceptor;import org.aopalliance.intercept.MethodInvocation;import org.apache.commons.lang.ClassUtils;import org.springframework.beans.factory.InitializingBean;import com.hoo.framework.log.ApplicationLogging;import com.hoo.framework.spring.support.CustomerContextHolder;/*** <b>function:</b> 動態設置數據源攔截器* @author hoojo* @createDate 2013-9-27 下午02:00:13* @file DataSourceMethodInterceptor.java* @package com.hoo.framework.spring.interceptor* @project SHMB* @blog http://blog.csdn.net/IBM_hoojo* @email hoojo_@126.com* @version 1.0*/public class DataSourceMethodInterceptor extends ApplicationLogging implements MethodInterceptor, InitializingBean {@Overridepublic Object invoke(MethodInvocation invocation) throws Throwable {Class<?> clazz = invocation.getThis().getClass();String className = clazz.getName();if (ClassUtils.isAssignable(clazz, Proxy.class)) {className = invocation.getMethod().getDeclaringClass().getName();}String methodName = invocation.getMethod().getName();Object[] arguments = invocation.getArguments();trace("execute {}.{}({})", className, methodName, arguments);if (className.contains("MySQL")) {CustomerContextHolder.setCustomerType(CustomerContextHolder.DATA_SOURCE_MYSQL);} else if (className.contains("Oracle")) {CustomerContextHolder.setCustomerType(CustomerContextHolder.DATA_SOURCE_ORACLE);} else if (methodName.contains("MySQL")) {CustomerContextHolder.setCustomerType(CustomerContextHolder.DATA_SOURCE_MYSQL);} else if (methodName.contains("Oracle")) {CustomerContextHolder.setCustomerType(CustomerContextHolder.DATA_SOURCE_ORACLE);} else {CustomerContextHolder.clearCustomerType();}/*if (className.contains("MySQL") || methodName.contains("MySQL")) {CustomerContextHolder.setCustomerType(CustomerContextHolder.DATA_SOURCE_MYSQL);} else if (className.contains("Oracle") || methodName.contains("Oracle")) {CustomerContextHolder.setCustomerType(CustomerContextHolder.DATA_SOURCE_ORACLE);} else {CustomerContextHolder.clearCustomerType();}*/Object result = invocation.proceed();return result;}@Overridepublic void afterPropertiesSet() throws Exception {log.trace("afterPropertiesSet……");}}
上面的代碼是在接口或實現中若是出現MySQL就設置數據源爲DATA_SOURCE_MYSQL,若是有Oracle就切換成DATA_SOURCE_ORACLE數據源。
四、編寫實際的業務接口和實現來測試攔截器是否有效
MultipleDataSourceService 接口
package com.hoo.server.datasource.service;/*** <b>function:</b> 多數據源測試服務接口* @author hoojo* @createDate 2013-10-10 上午11:07:31* @file MultipleDataSourceService.java* @package com.hoo.server.datasource.service* @project SHMB* @blog http://blog.csdn.net/IBM_hoojo* @email hoojo_@126.com* @version 1.0*/public interface MultipleDataSourceService {public void execute4MySQL() throws Exception;public void execute4Oracle() throws Exception;}
接口實現
package com.hoo.server.datasource.service.impl;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service;import com.hoo.framework.dao.BaseDao;import com.hoo.framework.service.impl.AbstractService;import com.hoo.server.datasource.service.MultipleDataSourceService;/*** <b>function:</b> 多數據源測試服務接口實現* @author hoojo* @createDate 2013-10-10 上午11:09:54* @file MultipleDataSourceServiceImpl.java* @package com.hoo.server.datasource.service.impl* @project SHMB* @blog http://blog.csdn.net/IBM_hoojo* @email hoojo_@126.com* @version 1.0*/@Servicepublic class MultipleDataSourceServiceImpl extends AbstractService implements MultipleDataSourceService {@Autowiredprivate BaseDao dao;@Overridepublic void execute4MySQL() throws Exception {info(dao.findBySql("select * from city limit 2").toString());}@Overridepublic void execute4Oracle() throws Exception {info(dao.findBySql("select * from devicestate_tab where rownum < 2").toString());}}
測試上面的服務層代碼,看看可否利用攔截器實現數據源動態切換
在上面的MultipleDataSourceServiceImplTest中加入以下代碼
@Autowired@Qualifier("multipleDataSourceServiceImpl")private MultipleDataSourceService service;@Testpublic void testService() {try {service.execute4MySQL();service.execute4Oracle();} catch (Exception e) {e.printStackTrace();}}
運行上面的代碼後能夠看到可以成功查詢到結果
五、測試實現類帶Oracle或MySQL字符串的
package com.hoo.server.datasource.service.impl;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service;import com.hoo.framework.dao.BaseDao;import com.hoo.framework.service.impl.AbstractService;import com.hoo.server.datasource.service.MultipleDataSourceService;/*** <b>function:</b> 多數據源測試服務接口實現* @author hoojo* @createDate 2013-10-10 上午11:09:54* @file MultipleDataSourceServiceImpl.java* @package com.hoo.server.datasource.service.impl* @project SHMB* @blog http://blog.csdn.net/IBM_hoojo* @email hoojo_@126.com* @version 1.0*/@Servicepublic class MySQLDataSourceServiceImpl extends AbstractService implements MultipleDataSourceService {@Autowiredprivate BaseDao dao;@Overridepublic void execute4MySQL() throws Exception {info(dao.findBySql("select * from city limit 2").toString());}@Overridepublic void execute4Oracle() throws Exception {info(dao.findBySql("select * from devicestate_tab where rownum < 2").toString());}}
package com.hoo.server.datasource.service.impl;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service;import com.hoo.framework.dao.BaseDao;import com.hoo.framework.service.impl.AbstractService;import com.hoo.server.datasource.service.MultipleDataSourceService;/*** <b>function:</b> 多數據源測試服務接口實現* @author hoojo* @createDate 2013-10-10 上午11:09:54* @file MultipleDataSourceServiceImpl.java* @package com.hoo.server.datasource.service.impl* @project SHMB* @blog http://blog.csdn.net/IBM_hoojo* @email hoojo_@126.com* @version 1.0*/@Servicepublic class OracleDataSourceServiceImpl extends AbstractService implements MultipleDataSourceService {@Autowiredprivate BaseDao dao;@Overridepublic void execute4MySQL() throws Exception {info(dao.findBySql("select * from city limit 2").toString());}@Overridepublic void execute4Oracle() throws Exception {info(dao.findBySql("select * from devicestate_tab where rownum < 2").toString());}}
這裏的兩個實現類的類名都含有不一樣規則的數據源標識符字符串,並且方法名也含有相關字符串,這些都匹配攔截器中的規則。
在MultipleDataSourceServiceImplTest 中加入測試代碼
@Autowired@Qualifier("oracleDataSourceServiceImpl")private MultipleDataSourceService oracleService;@Autowired@Qualifier("mySQLDataSourceServiceImpl")private MultipleDataSourceService mySQLService;@Testpublic void testOracleService() {try {oracleService.execute4MySQL();} catch (Exception e1) {e1.printStackTrace();}try {oracleService.execute4Oracle();} catch (Exception e) {e.printStackTrace();}}@Testpublic void testMySQLService() {try {mySQLService.execute4MySQL();} catch (Exception e1) {e1.printStackTrace();}try {mySQLService.execute4Oracle();} catch (Exception e) {e.printStackTrace();}}
執行上面的測試用例會發現有一個查詢會失敗,那是由於咱們按照攔截器中的業務規則切換數據源就匹配到了其中一個,就是經過類名進行數據源切換,因此只定位到其中一個數據源。
六、測試MyBatis的數據源切換方法
MyBatis的查詢接口
package com.hoo.server.datasource.mapper;import java.util.List;import java.util.Map;import com.hoo.framework.mybatis.SqlMapper;/*** <b>function:</b> MyBatis 多數據源 測試查詢接口* @author hoojo* @createDate 2013-10-10 下午04:18:08* @file MultipleDataSourceMapper.java* @package com.hoo.server.datasource.mapper* @project SHMB* @blog http://blog.csdn.net/IBM_hoojo* @email hoojo_@126.com* @version 1.0*/public interface MultipleDataSourceMapper extends SqlMapper {public List<Map<String, Object>> execute4MySQL() throws Exception;public List<Map<String, Object>> execute4Oracle() throws Exception;}
multiple-datasource-mapper.xml
<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.hoo.server.datasource.mapper.MultipleDataSourceMapper"><select id="execute4Oracle" resultType="map"><![CDATA[SELECT*FROMdeviceInfo_tab t where rownum < 10]]></select><select id="execute4MySQL" resultType="map"><![CDATA[SELECT*FROMcity limit 2]]></select></mapper>
測試MyBatis的mapper查詢接口,在MultipleDataSourceServiceImplTest加入如下代碼
@Autowiredprivate MultipleDataSourceMapper mapper;@Testpublic void testMapper() {try {trace(mapper.execute4MySQL());} catch (Exception e1) {e1.printStackTrace();}try {trace(mapper.execute4Oracle());} catch (Exception e) {e.printStackTrace();}}
運行以上測試代碼也能發現能夠正常的查詢到Oracle和MySQL數據庫中的數據。MyBatis的在這裏只負責查詢,而增刪改是hibernate完成的任務,因此這裏也就再也不測試modified部分。
七、上面的攔截器是須要在配置文件中進行配置的,這裏利用annotation的配置的攔截器進行業務攔截,也許有些人更喜歡用annotation
package com.hoo.framework.spring.interceptor;import java.lang.reflect.Proxy;import org.apache.commons.lang.ClassUtils;import org.aspectj.lang.JoinPoint;import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.Before;import org.springframework.stereotype.Component;import com.hoo.framework.log.ApplicationLogging;import com.hoo.framework.spring.support.CustomerContextHolder;/*** <b>function:</b> 多數據源動態配置攔截器* @author hoojo* @createDate 2013-10-10 上午11:35:54* @file MultipleDataSourceInterceptor.java* @package com.hoo.framework.spring.interceptor* @project SHMB* @blog http://blog.csdn.net/IBM_hoojo* @email hoojo_@126.com* @version 1.0*/@Component@Aspectpublic class MultipleDataSourceInterceptor extends ApplicationLogging {/*** <b>function:</b> 動態設置數據源* @author hoojo* @createDate 2013-10-10 上午11:38:45* @throws Exception*/@Before("execution(* com.hoo..service.impl.*ServiceImpl.*(..)) || execution(* com.hoo..mapper.*Mapper.*(..))")public void dynamicSetDataSoruce(JoinPoint joinPoint) throws Exception {Class<?> clazz = joinPoint.getTarget().getClass();String className = clazz.getName();if (ClassUtils.isAssignable(clazz, Proxy.class)) {className = joinPoint.getSignature().getDeclaringTypeName();}String methodName = joinPoint.getSignature().getName();Object[] arguments = joinPoint.getArgs();trace("execute {}.{}({})", className, methodName, arguments);if (className.contains("MySQL")) {CustomerContextHolder.setCustomerType(CustomerContextHolder.DATA_SOURCE_MYSQL);} else if (className.contains("Oracle")) {CustomerContextHolder.setCustomerType(CustomerContextHolder.DATA_SOURCE_ORACLE);} else if (methodName.contains("MySQL")) {CustomerContextHolder.setCustomerType(CustomerContextHolder.DATA_SOURCE_MYSQL);} else if (methodName.contains("Oracle")) {CustomerContextHolder.setCustomerType(CustomerContextHolder.DATA_SOURCE_ORACLE);} else {CustomerContextHolder.clearCustomerType();}/*if (className.contains("MySQL") || methodName.contains("MySQL")) {CustomerContextHolder.setCustomerType(CustomerContextHolder.DATA_SOURCE_MYSQL);} else if (className.contains("Oracle") || methodName.contains("Oracle")) {CustomerContextHolder.setCustomerType(CustomerContextHolder.DATA_SOURCE_ORACLE);} else {CustomerContextHolder.clearCustomerType();}*/}}
這種攔截器就是不須要在配置文件中加入任何配置進行攔截,算是一種擴展的方法。
3、總結
多數據源動態切換的主要地方在於咱們要定義一個本身的數據源來實現AbstractRoutingDataSource中的determineCurrentLookupKey方法,而後經過CustomerContextHolder來實現數據源的切換工做。而數據源的動態切換也就在於咱們利用了Spring的Aop中的攔截器Interceptor進行業務類的方法進行攔截,經過類名或方法名中的有效字符串來動態切換到咱們定義好的規則對應的數據源。