打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
Spring多数据源配置实现

Spring+MyBatis多数据源配置实现

最近用到了MyBatis配置多数据源,原以为简单配置下就行了,实际操作后发现还是要费些事的,这里记录下,以作备忘

不多废话,直接上代码,后面会有简单的实现介绍

jdbc和log4j的配置

 log4j.properties
#============================================================================# MySQL#============================================================================jdbc.mysql.driver=com.mysql.jdbc.Driverjdbc.mysql.url=jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=truejdbc.mysql.username=rootjdbc.mysql.password=root#============================================================================# MS SQL Server#============================================================================#jdbc.sqlserver.driver=com.microsoft.sqlserver.jdbc.SQLServerDriver#jdbc.sqlserver.url=jdbc:sqlserver://127.0.0.1:1433;database=test;#jdbc.sqlserver.username=sa#jdbc.sqlserver.password=sa#============================================================================# MS SQL Server (JTDS)#============================================================================jdbc.sqlserver.driver=net.sourceforge.jtds.jdbc.Driverjdbc.sqlserver.url=jdbc:jtds:sqlserver://127.0.0.1:1433/testjdbc.sqlserver.username=sajdbc.sqlserver.password=sa#============================================================================# 通用配置#============================================================================jdbc.initialSize=5jdbc.minIdle=5jdbc.maxIdle=20jdbc.maxActive=100jdbc.maxWait=100000jdbc.defaultAutoCommit=falsejdbc.removeAbandoned=truejdbc.removeAbandonedTimeout=600jdbc.testWhileIdle=truejdbc.timeBetweenEvictionRunsMillis=60000jdbc.numTestsPerEvictionRun=20jdbc.minEvictableIdleTimeMillis=300000

单数据源时的Spring配置文件

 applicationContext.xml

多数据源时Spring配置文件

<?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:context="http://www.springframework.org/schema/context"       xmlns:aop="http://www.springframework.org/schema/aop"       xsi:schemaLocation="http://www.springframework.org/schema/beans    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd    http://www.springframework.org/schema/context    http://www.springframework.org/schema/context/spring-context-3.0.xsd      http://www.springframework.org/schema/aop          http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">    <bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">        <property name="location" value="classpath:jdbc.properties"/>    </bean>    <bean id="sqlServerDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">        <property name="driverClassName" value="${jdbc.sqlserver.driver}"/>        <property name="url" value="${jdbc.sqlserver.url}"/>        <property name="username" value="${jdbc.sqlserver.username}"/>        <property name="password" value="${jdbc.sqlserver.password}"/>        <property name="initialSize" value="${jdbc.initialSize}"/>        <property name="minIdle" value="${jdbc.minIdle}"/>        <property name="maxIdle" value="${jdbc.maxIdle}"/>        <property name="maxActive" value="${jdbc.maxActive}"/>        <property name="maxWait" value="${jdbc.maxWait}"/>        <property name="defaultAutoCommit" value="${jdbc.defaultAutoCommit}"/>        <property name="removeAbandoned" value="${jdbc.removeAbandoned}"/>        <property name="removeAbandonedTimeout" value="${jdbc.removeAbandonedTimeout}"/>        <property name="testWhileIdle" value="${jdbc.testWhileIdle}"/>        <property name="timeBetweenEvictionRunsMillis" value="${jdbc.timeBetweenEvictionRunsMillis}"/>        <property name="numTestsPerEvictionRun" value="${jdbc.numTestsPerEvictionRun}"/>        <property name="minEvictableIdleTimeMillis" value="${jdbc.minEvictableIdleTimeMillis}"/>    </bean>    <bean id="mySqlDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">        <property name="driverClassName" value="${jdbc.mysql.driver}"/>        <property name="url" value="${jdbc.mysql.url}"/>        <property name="username" value="${jdbc.mysql.username}"/>        <property name="password" value="${jdbc.mysql.password}"/>        <property name="initialSize" value="${jdbc.initialSize}"/>        <property name="minIdle" value="${jdbc.minIdle}"/>        <property name="maxIdle" value="${jdbc.maxIdle}"/>        <property name="maxActive" value="${jdbc.maxActive}"/>        <property name="maxWait" value="${jdbc.maxWait}"/>        <property name="defaultAutoCommit" value="${jdbc.defaultAutoCommit}"/>        <property name="removeAbandoned" value="${jdbc.removeAbandoned}"/>        <property name="removeAbandonedTimeout" value="${jdbc.removeAbandonedTimeout}"/>        <property name="testWhileIdle" value="${jdbc.testWhileIdle}"/>        <property name="timeBetweenEvictionRunsMillis" value="${jdbc.timeBetweenEvictionRunsMillis}"/>        <property name="numTestsPerEvictionRun" value="${jdbc.numTestsPerEvictionRun}"/>        <property name="minEvictableIdleTimeMillis" value="${jdbc.minEvictableIdleTimeMillis}"/>    </bean>    <bean id="multipleDataSource" class="com.cnblogs.lzrabbit.MultipleDataSource">        <property name="defaultTargetDataSource" ref="mySqlDataSource"/>        <property name="targetDataSources">            <map>                <entry key="mySqlDataSource" value-ref="mySqlDataSource"/>                <entry key="sqlServerDataSource" value-ref="sqlServerDataSource"/>            </map>        </property>    </bean>    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">        <property name="dataSource" ref="multipleDataSource"/>    </bean>    <!-- mybatis.spring自动映射 -->    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">        <property name="basePackage" value="com.cnblogs.lzrabbit"/>    </bean>    <!-- 自动扫描,多个包以 逗号分隔 -->    <context:component-scan base-package="com.cnblogs.lzrabbit"/>    <aop:aspectj-autoproxy/></beans>

MultipleDataSource实现

package com.cnblogs.lzrabbit;import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;/** * Created by rabbit on 14-5-25. */public class MultipleDataSource extends AbstractRoutingDataSource {    private static final ThreadLocal<String> dataSourceKey = new InheritableThreadLocal<String>();    public static void setDataSourceKey(String dataSource) {        dataSourceKey.set(dataSource);    }    @Override    protected Object determineCurrentLookupKey() {        return dataSourceKey.get();    }}

MyBatis接口Mapper定义,直接使用注解方式实现

public interface MySqlMapper {    @Select("select * from MyTable")    List<Map<String,Object>> getList();}public interface SqlServerMapper {        @Select("select * from MyTable")        List<Map<String,Object>> getList();}

手动数据源切换调用

package com.cnblogs.lzrabbit;import org.springframework.context.ApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;/** * Created by rabbit on 14-5-25. */public class Main {    public static void main(String[] args) {        //初始化ApplicationContext        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");        MySqlMapper mySqlMapper = applicationContext.getBean(MySqlMapper.class);        SqlServerMapper sqlServerMapper = applicationContext.getBean(SqlServerMapper.class);                //设置数据源为MySql,使用了AOP测试时请将下面这行注释        MultipleDataSource.setDataSourceKey("mySqlDataSource");        mySqlMapper.getList();        //设置数据源为SqlServer,使用AOP测试时请将下面这行注释        MultipleDataSource.setDataSourceKey("sqlServerDataSource");        sqlServerMapper.getList();    }}

使用SpringAOP方式实现自动切换

package com.cnblogs.lzrabbit;import org.aspectj.lang.ProceedingJoinPoint;import org.aspectj.lang.annotation.Around;import org.aspectj.lang.annotation.Aspect;import org.springframework.stereotype.Component;@Component@Aspectpublic class MultipleDataSourceAspectAdvice {    @Around("execution(* com.cnblogs.lzrabbit.*.*(..))")    public Object doAround(ProceedingJoinPoint jp) throws Throwable {        if (jp.getTarget() instanceof MySqlMapper) {            MultipleDataSource.setDataSourceKey("mySqlDataSource");        } else if (jp.getTarget() instanceof SqlServerMapper) {            MultipleDataSource.setDataSourceKey("sqlServerDataSource");        }        return jp.proceed();    }}

调用日志

2014-05-25 20:02:04,319 DEBUG [main] com.cnblogs.lzrabbit.MySqlMapper.getList - ooo Using Connection [jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true, UserName=root@192.168.1.32, MySQL Connector Java]2014-05-25 20:02:04,333 DEBUG [main] com.cnblogs.lzrabbit.MySqlMapper.getList - ==>  Preparing: select * from MyTable 2014-05-25 20:02:04,371 DEBUG [main] com.cnblogs.lzrabbit.MySqlMapper.getList - ==> Parameters: 2014-05-25 20:02:04,396 DEBUG [main] com.cnblogs.lzrabbit.MySqlMapper.getList - <==      Total: 82014-05-25 20:02:04,620 DEBUG [main] com.cnblogs.lzrabbit.SqlServerMapper.getList - ooo Using Connection [jdbc:jtds:sqlserver://127.0.0.1:1433/test, UserName=sa, jTDS Type 4 JDBC Driver for MS SQL Server and Sybase]2014-05-25 20:02:04,620 DEBUG [main] com.cnblogs.lzrabbit.SqlServerMapper.getList - ==>  Preparing: select * from TmallCityMap 2014-05-25 20:02:04,621 DEBUG [main] com.cnblogs.lzrabbit.SqlServerMapper.getList - ==> Parameters: 2014-05-25 20:02:04,681 DEBUG [main] com.cnblogs.lzrabbit.SqlServerMapper.getList - <==      Total: 397

这里就上面的实现做个简单解释,在我们配置单数据源时可以看到数据源类型使用了org.apache.commons.dbcp.BasicDataSource,而这个代码实现了javax.sql.DataSource接口

配置sqlSessionFactory时org.mybatis.spring.SqlSessionFactoryBean注入参数dataSource类型就是javax.sql.DataSource

实现多数据源的方法就是我们自定义了一个MultipleDataSource,这个类继承自AbstractRoutingDataSource,而AbstractRoutingDataSource继承自AbstractDataSource ,AbstractDataSource 实现了javax.sql.DataSource接口,所以我们的MultipleDataSource也实现了javax.sql.DataSource接口,可以赋值给sqlSessionFactory的dataSource属性

public abstract class AbstractRoutingDataSource extends AbstractDataSource implements InitializingBean {}public abstract class AbstractDataSource implements DataSource {}

  再来说下MultipleDataSource的实现原理,MultipleDataSource实现AbstractRoutingDataSource抽象类,然后实现了determineCurrentLookupKey方法,这个方法用于选择具体使用targetDataSources中的哪一个数据源

<bean id="multipleDataSource" class="com.cnblogs.lzrabbit.MultipleDataSource">        <property name="defaultTargetDataSource" ref="mySqlDataSource"/>        <property name="targetDataSources">            <map>                <entry key="mySqlDataSource" value-ref="mySqlDataSource"/>                <entry key="sqlServerDataSource" value-ref="sqlServerDataSource"/>            </map>        </property>    </bean>

可以看到Spring配置中multipleDataSource设置了两个属性defaultTargetDataSource和targetDataSources,这两个属性定义在AbstractRoutingDataSource,当MyBatis执行查询时会先选择数据源,选择顺序时现根据determineCurrentLookupKey方法返回的值到targetDataSources中去找,若能找到怎返回对应的数据源,若找不到返回默认的数据源defaultTargetDataSource,具体参考AbstractRoutingDataSource的源码

public abstract class AbstractRoutingDataSource extends AbstractDataSource implements InitializingBean {    private Map<Object, Object> targetDataSources;    private Object defaultTargetDataSource;        /**     * Retrieve the current target DataSource. Determines the     * {@link #determineCurrentLookupKey() current lookup key}, performs     * a lookup in the {@link #setTargetDataSources targetDataSources} map,     * falls back to the specified     * {@link #setDefaultTargetDataSource default target DataSource} if necessary.     * @see #determineCurrentLookupKey()     */    protected DataSource determineTargetDataSource() {        Assert.notNull(this.resolvedDataSources, "DataSource router not initialized");        Object lookupKey = determineCurrentLookupKey();        DataSource dataSource = this.resolvedDataSources.get(lookupKey);        if (dataSource == null && (this.lenientFallback || lookupKey == null)) {            dataSource = this.resolvedDefaultDataSource;        }        if (dataSource == null) {            throw new IllegalStateException("Cannot determine target DataSource for lookup key [" + lookupKey + "]");        }        return dataSource;    }    /**     * Determine the current lookup key. This will typically be     * implemented to check a thread-bound transaction context.     * <p>Allows for arbitrary keys. The returned key needs     * to match the stored lookup key type, as resolved by the     * {@link #resolveSpecifiedLookupKey} method.     */    protected abstract Object determineCurrentLookupKey();
  
  .............}
本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
Spring多数据源的配置和使用
Spring常用的接口和类
配置Spring数据源c3p0与dbcp
spring+ibatis多数据源
Java程序中连接池、及参数绑定实现
mybatis、ibatis、spring各种整合方式
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服