Spring MVC 动态切换数据库

项目中遇到需要动态切换数据库的需求,在特定的界面需要去另外一个数据库查询信息
直接上代码
1.新建DataSourceType .java(配置数据库名称)

package com.grand.datasourse;
/**
 * 数据库名称 -需要合配置文件里的名称一致
 * @author 
 *
 * @time 2017年10月23日
 */
public class DataSourceType {

    public static final String SOURCE_UWP = "uwp_dbsourse";//主数据库
    public static final String SOURCE_WD = "wd_dbsourse";//从数据库

}

2.新建DataSourceContextHolder .java 切换数据库的类

package com.grand.datasourse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


/**
 * 
 * @author  数据库切换的工具类:切换数据库
 * 
 * @time 2017年10月22日
 */
public class DataSourceContextHolder {
    //线程局部变量
    private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>();
    private static final Logger LOG = LoggerFactory
            .getLogger(DataSourceContextHolder.class);
    /**
     * 提供给AOP去设置当前的线程的数据源的信息
     * 切换数据库
     * @param dbType 数据库名-配置文件中一致
     */
    public static void setDbType(String dbType) {
        try {
            contextHolder.set(dbType);
        } catch (Exception e) {
            // TODO: handle exception
            LOG.debug("ERROR____DB_Exception");
            LOG.debug(e.getMessage());
        }

    }

    /**
     * 提供给AbstractRoutingDataSource的实现类,通过key选择数据源
     * @return java.lang.String
     */
    public static String getDbType() {
        String dataSourse  =  contextHolder.get();

        if(null == dataSourse){

            DataSourceContextHolder.setDbType(DataSourceType.SOURCE_UWP);
        }

        return  contextHolder.get();
    }

    /**
     * 使用默认的数据源
     */
    public static void clearDbType() {
        contextHolder.remove();
    }
}

3.新建DynamicDataSource.java 数据源配置

package com.grand.datasourse;

import java.util.logging.Logger;

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

/**
 * descrption: 多数据源的选择
 * @author 
 *  AbstractRoutingDataSource中的抽象方法determineCurrentLookupKey是实现数据源的route的核心
 *               .需要重写该方法
 * @time 2017年10月22日
 */
public class DynamicDataSource extends AbstractRoutingDataSource{

    @Override  
    public Logger getParentLogger() {  
           return null;  
    }  

    @Override  
    protected Object determineCurrentLookupKey() {  
           return DataSourceContextHolder.getDbType();//获取数据源  
    } 

}

4.applicationContext.xml 里配置数据源

<!--主库-->
<bean id ="uwp_dbsourse" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">  
          <property name="driverClass" value="${jdbc.driverClassName2}" />
        <property name="jdbcUrl" value="${jdbc.url2}" />
        <property name="user" value="${jdbc.userDS.username2}" />
        <property name="password" value="${jdbc.userDS.password2}" />
        <!-- 连接池中保留的最小连接数    -->
        <property name="minPoolSize" value="1" />
        <!-- 连接池中保留的最大连接数    -->
        <property name="maxPoolSize" value="200" />
        <!-- 最大空闲时间,1800秒内未使用则连接被丢弃。若为0则永不丢弃。Default: 0-->
        <property name="maxIdleTime" value="1800" />
        <!-- 当连接池中的连接耗尽的时候c3p0一次同时获取的连接数。Default: 3-->
        <property name="acquireIncrement" value="5" />
        <!-- 最大的PreparedStatement的数量    -->
        <property name="maxStatements" value="1000" />
        <!-- 每60秒检查所有连接池中的空闲连接。Default: 0 -->
        <property name="idleConnectionTestPeriod" value="60" />
        <!-- 定义在从数据库获取新连接失败后重复尝试的次数。Default: 30-->
        <property name="acquireRetryAttempts" value="30" />
        <property name="breakAfterAcquireFailure" value="true" />
        <property name="testConnectionOnCheckout" value="false" />
   </bean > 
   <!--从库--> 
   <bean id ="wd_dbsourse" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">  
          <property name="driverClass" value="${jdbc.driverClassName1}" />
        <property name="jdbcUrl" value="${jdbc.url1}" />
        <property name="user" value="${jdbc.userDS.username1}" />
        <property name="password" value="${jdbc.userDS.password1}" />
        <!-- 连接池中保留的最小连接数    -->
        <property name="minPoolSize" value="1" />
        <!-- 连接池中保留的最大连接数    -->
        <property name="maxPoolSize" value="200" />
        <!-- 最大空闲时间,1800秒内未使用则连接被丢弃。若为0则永不丢弃。Default: 0-->
        <property name="maxIdleTime" value="1800" />
        <!-- 当连接池中的连接耗尽的时候c3p0一次同时获取的连接数。Default: 3-->
        <property name="acquireIncrement" value="5" />
        <!-- 最大的PreparedStatement的数量    -->
        <property name="maxStatements" value="1000" />
        <!-- 每60秒检查所有连接池中的空闲连接。Default: 0 -->
        <property name="idleConnectionTestPeriod" value="60" />
        <!-- 定义在从数据库获取新连接失败后重复尝试的次数。Default: 30-->
        <property name="acquireRetryAttempts" value="30" />
        <property name="breakAfterAcquireFailure" value="true" />
        <property name="testConnectionOnCheckout" value="false" />
   </bean > 

    <!-- 动态配置数据源 -->  
   <bean id ="dataSource" class= "com.grand.datasourse.DynamicDataSource" >  
          <property name ="targetDataSources">  
                <map key-type ="java.lang.String">  
                      <entry value-ref ="wd_dbsourse" key= "wd_dbsourse"></entry >  
                      <entry value-ref ="uwp_dbsourse" key= "uwp_dbsourse"></entry >  
                </map >  
          </property >  
          <property name ="defaultTargetDataSource" ref= "uwp_dbsourse"></property >      <!-- 默认使用的数据源 -->  
   </bean > 

5.service 层调用

public List<Test> getTest(){

        List<Test> list = null;
        try {
            // 切换数据库
            DataSourceContextHolder.setDbType(DataSourceType.SOURCE_WD);




            list = wdFundMapper.getTest();

        } catch (Exception e) {
            e.printStackTrace();
            LOG.error(e.getMessage());
        }
        DataSourceContextHolder.clearDbType();//切回主库
        return list;
    }

注意:项目中发现,这种service层调用方式,存在小瑕疵,在同一个service 中,只能调用一个库,无法进行切库操作
例如:A service中,调用主库
B service中,掉用从库
A 中调用B的方法,往往失败
本人的做法是,将调用从库的方法统一写在B中,在控制层同时调用AB方法,将数据在控制层做区分

猜你喜欢

转载自blog.csdn.net/sinat_34979884/article/details/79133539