一、开篇

这里整合分别采用了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

<?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/beans 

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

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

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

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

    http://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&amp;characterEncoding=UTF-8&amp;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 {

 

    @Override

    protected 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 {

 

    @Autowired

    private BaseDao dao;

      

    @Test

    public 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.xsd

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

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

    http://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 {

 

    @Override

    public 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;

    }

 

    @Override

    public 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

 */

@Service

public class MultipleDataSourceServiceImpl extends AbstractService implements MultipleDataSourceService {

    

    @Autowired

    private BaseDao dao;

    

    @Override

    public void execute4MySQL() throws Exception {

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

    }

 

    @Override

    public void execute4Oracle() throws Exception {

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

    }

}

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

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

@Autowired

@Qualifier("multipleDataSourceServiceImpl")

private MultipleDataSourceService service;

 

@Test

public 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

 */

@Service

public class MySQLDataSourceServiceImpl extends AbstractService implements MultipleDataSourceService {

    

    @Autowired

    private BaseDao dao;

    

    @Override

    public void execute4MySQL() throws Exception {

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

    }

 

    @Override

    public 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

 */

@Service

public class OracleDataSourceServiceImpl extends AbstractService implements MultipleDataSourceService {

    

    @Autowired

    private BaseDao dao;

    

    @Override

    public void execute4MySQL() throws Exception {

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

    }

 

    @Override

    public 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;

 

@Test

public void testOracleService() {

    try {

        oracleService.execute4MySQL();

    } catch (Exception e1) {

        e1.printStackTrace();

    }

    try {

        oracleService.execute4Oracle();

    } catch (Exception e) {

        e.printStackTrace();

    }

}

 

@Test

public 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

                *

            FROM

                deviceInfo_tab t where rownum < 10

        ]]>

    </select>

    

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

        <![CDATA[

            SELECT

                *

            FROM

                city limit 2

        ]]>

    </select>

</mapper>

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

@Autowired

private MultipleDataSourceMapper mapper;

 

@Test

public 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

@Aspect

public 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 配置多数据源/动态切换数据源 方法的更多相关文章

  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. 咏南中间件+开发框架支持最新的DELPHI XE10.1(BERLIN) UPDATE1

    咏南中间件+开发框架支持最新的DELPHI XE10.1(BERLIN) UPDATE1 购买提供:中间件源码,价格十分优惠!有意者请向本人索取演示程序! 附带福利(赠送): CS开发框架源码BS开发 ...

  2. mybatis mapper.xml 配置文件问题(有的错误xml是不报的) 导致服务无法启动 。

    转载自 开源编程 一舟mybatsi xml编译报错,tomcat启动一直循环,导致内存溢出,启动失败 mapper.xml怎么知道有没有编译错误,哪个位置有错误 这应该是mybatis的一个bug, ...

  3. 【html】:禁止鼠标事件

    <body oncontextmenu="return false" onselectstart="return false" ondragstart=& ...

  4. 关于collapsed margin(外边距合并)

    这是前面写postion定位时写到最后面的例子的时候发现的一个问题,于是专门写一篇随笔来解释记录一下,毕竟两个知识点同时写在一篇文章里面有点混乱的感觉.. 上篇随笔position定位遇到的问题在这里 ...

  5. linux 中如何修改时间 date

    修改linux的时间可以使用date指令  修改日期: 时间设定成2009年5月10日的命令如下: #date -s 05/10/2009  修改时间: 将系统时间设定成上午10点18分0秒的命令如下 ...

  6. WordPaster-Firefox浏览器控件安装方法

    将WordPaster.xpi拖到Firefox扩展面板中安装       新版Firefox可能无法通过验证,如果Firefox提示无法安装,则需要进入about:config中将xpinstall ...

  7. 元数据集 DatabaseMetaData ResultSetMetaData

  8. 基于VC的声音文件操作(三)

    (四)VC中相关的操作 1.mmioOpen 打开一个文件 Syntax MMIO mmioOpen( LPTSTR szFilename, LPMMIOINFO lpmmioinfo, DWORD ...

  9. Objective C笔记(第一天)

    • OC语言概述 1.早在20世纪80年代早期,Bard Cox发明了Objective C, 是扩充的C,面向对象的编程语言. 2.NEXTSTEP简称NS a.1985年,Steve Jobs成⽴ ...

  10. Swift闭包概念与常见使用场景总结

    ·Swift 闭包 闭包(Closures)是自包含的功能代码块,可以在代码中使用或者用来作为参数传值. Swift 中的闭包与 C 和 Objective-C 中的代码块(blocks)以及其他一些 ...