Spring3.3 整合 Hibernate3、MyBatis3.2 配置多数据源/动态切换数据源 方法
一、开篇
这里整合分别采用了Hibernate和MyBatis两大持久层框架,Hibernate主要完成增删改功能和一些单一的对象查询功能,MyBatis主要负责查询功能。所以在出来数据库方言的时候基本上没有什么问题,但唯一可能出现问题的就是在hibernate做添加操作生成主键策略的时候。因为我们都知道hibernate的数据库本地方言会针对不同的数据库采用不同的主键生成策略。
所以针对这一问题不得不采用自定义的主键生成策略,自己写一个主键生成器的表来维护主键生成方式或以及使用其他的方式来生成主键,从而避开利用hibernate默认提供的主键生成方式。
所以存在问题有:怎样动态的切换数据库方言?
这个问题还没有解决,没有更多时间来研究。不过想想应该可以配置两个SessionFactory来实现,那又存在怎么样动态切换SessionFactory呢?!需要解决这个问题才行,而这里则演示怎么样动态切换DataSource数据源的方法。
二、代码演示
在演示开始之前你的项目已经成功的整合完成的情况下才行,如果你还不知道怎么使用Spring整合MyBatis和Spring整合Hibernate的话。建议参考之前的文章:MyBatis3整合Spring3、SpringMVC3、Struts2、Spring、Hibernate整合ExtJS这两篇文章结合起来就可以完成整合是几大框架了。这里重点介绍动态切换DataSource数据源的方法!
1、datasource的配置 applicationContext-datasource.xml
<?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实现抽象方法即可。
2、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,这个很关键不要搞错了。
3、测试看能否切换数据源
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在这里是一个很好的选择。它可以在方法之前或方法之后完成一些操作,而且控制的粒度可以细到具体的方法中的参数、返回值、方法名等。在这里控制数据源动态切换最好不过了!
4、上面是手动切换数据源,我们用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拦截到,并通过其中的规律动态设置数据源。
3、拦截器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数据源。
4、编写实际的业务接口和实现来测试拦截器是否有效
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();}}运行上面的代码后可以看到能够成功查询到结果
5、测试实现类带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();}}执行上面的测试用例会发现有一个查询会失败,那是因为我们按照拦截器中的业务规则切换数据源就匹配到了其中一个,就是通过类名进行数据源切换,所以只定位到其中一个数据源。
6、测试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部分。
7、上面的拦截器是需要在配置文件中进行配置的,这里利用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();}*/}}这种拦截器就是不需要在配置文件中加入任何配置进行拦截,算是一种扩展的方法。
三、总结
多数据源动态切换的主要地方在于我们要定义一个自己的数据源来实现AbstractRoutingDataSource中的determineCurrentLookupKey方法,然后通过CustomerContextHolder来实现数据源的切换工作。而数据源的动态切换也就在于我们利用了Spring的Aop中的拦截器Interceptor进行业务类的方法进行拦截,通过类名或方法名中的有效字符串来动态切换到我们定义好的规则对应的数据源。
Spring3.3 整合 Hibernate3、MyBatis3.2 配置多数据源/动态切换数据源 方法的更多相关文章
- Spring3.3 整合 Hibernate3、MyBatis3.2 配置多数据源/动态切换数据源方法
一.开篇 这里整合分别采用了Hibernate和MyBatis两大持久层框架,Hibernate主要完成增删改功能和一些单一的对象查询功能,MyBatis主要负责查询功能.所以在出来数据库方言的时候基 ...
- springmvc+mybatis多数据源配置,AOP注解动态切换数据源
springmvc与springboot没多大区别,springboot一个jar包配置几乎包含了所有springmvc,也不需要繁琐的xml配置,springmvc需要配置多种jar包,需要繁琐的x ...
- spring+myBatis 配置多数据源,切换数据源
注:本文来源于 tianzhiwuqis <spring+myBatis 配置多数据源,切换数据源> 一个项目里一般情况下只会使用到一个数据库,但有的需求是要显示其他数据库的内容,像这样 ...
- Springboot多数据源配置--数据源动态切换
在上一篇我们介绍了多数据源,但是我们会发现在实际中我们很少直接获取数据源对象进行操作,我们常用的是jdbcTemplate或者是jpa进行操作数据库.那么这一节我们将要介绍怎么进行多数据源动态切换.添 ...
- Spring Boot数据访问之多数据源配置及数据源动态切换
如果一个数据库数据量过大,考虑到分库分表和读写分离需要动态的切换到相应的数据库进行相关操作,这样就会有多个数据源.对于一个数据源的配置在Spring Boot数据访问之数据源自动配置 - 池塘里洗澡的 ...
- springboot整合druid连接池、mybatis实现多数据源动态切换
demo环境: JDK 1.8 ,Spring boot 1.5.14 一 整合durid 1.添加druid连接池maven依赖 <dependency> <groupId> ...
- [转]SpringMVC+ Mybatis 配置多数据源 + 手动切换数据源
正确可行的解决方法:使用Spring提供的AbstractRoutingDataSource类来根据请求路由到不同的数据源.具体做法是先设置两个不同的dataSource代表不同的数据源,再建一个总的 ...
- spring-boot整合mybaits多数据源动态切换案例
1.运行环境 开发工具:intellij idea JDK版本:1.8 项目管理工具:Maven 4.0.0 2.GITHUB地址 https://github.com/nbfujx/springBo ...
- Spring3 整合MyBatis3 配置多数据源 动态选择SqlSessionFactory
一.摘要 上两篇文章分别介绍了Spring3.3 整合 Hibernate3.MyBatis3.2 配置多数据源/动态切换数据源 方法 和 Spring3 整合Hibernate3.5 动态切换Ses ...
随机推荐
- zend 汉化
http://jingyan.baidu.com/article/4f34706ecdb566e387b56d05.html
- FreeBSD_11-系统管理——{Part_3-网络}
一.Network Servers DNS unbound/local_unbound # /etc/rc.conf local_unbound_enable="YES" # 测试 ...
- Mac 系统下类似于 apt-get 的软件包管理器 -- Homebrew
对于一个习惯了在 Ubuntu 的终端上通过 apt-get 来安装工具软件的我来说,也希望在Mac上找到类似的工具,能很方便的一条命令就能安装所需的软件,而不用手工的去查找下载编译,或者是折腾安装所 ...
- [Chapter 3 Process]Practice 3.12 Including the initial parent process, how many processes are created by the program shown in Figure 3.32?
3.12 Including the initial parent process, how many processes are created by the program shown in Fi ...
- 利用Java自带的MD5加密
package test.md5; import java.security.MessageDigest; public class MD5Util { public final static Str ...
- step byt step之餐饮管理系统一
之前写过2015年的工作计划,其中有一项就是写一套管理系统,一来可以练练手,二来可以加强自己的学习,三来可以多园友多交流,共同进步.所以从今天开始把写系统的过程记录下来.先需求分析开始. 第一部分 引 ...
- linux samba 服务器 简单配置
1. rpm -qa|grep samba 查看是否有samba 2.使用yum -y install samba 安装samba 服务 进入/etc/samba/ 使用vi smb.conf 修改配 ...
- keil 的头文件 .
许多初学者使用网上下载的程序时都会遇到这样一个问题,就是头文件找不到.我想就这个问题说明一下./·首先,我们用到的KEIL有几种版本的,头文件也不同.有reg51.h和at89x51.h两种比较常见. ...
- error while loading shared libraries: xxx.so.x"错误的原因和解决办法
http://blog.chinaunix.net/uid-26212859-id-3256667.html 参考博客 http://hi.baidu.com/newdreamllc/item/687 ...
- Java Config 下的Spring Test方式
用了三种方式: 1.纯手动取bean: package com.wang.test; import com.marsmother.commission.core.config.MapperConfig ...