SpringMVC+Mybatis 如何配置多个数据源并切换

夙愿已清 提交于 2020-01-08 15:00:11

SpringMVC+Mybatis 如何配置多个数据源并切换?

摘自:
https://www.cnblogs.com/haha12/p/10613549.html

#使用场景
多数据源的使用的场景一般有:

主从数据库切换
读写分离
兼容旧库
实现原理
Spring2.x的版本中采用Proxy模式,就是在方案中实现一个虚拟的数据源,并且用它来封装数据源选择逻辑,这样就可以有效地将数据源选择逻辑从Client中分离出来。Client提供选择所需的上下文,由虚拟的DynamicDataSource根据Client提供的上下文来实现数据源的选择。

具体的实现是虚拟的DynamicDataSource仅需继承AbstractRoutingDataSource实现determineCurrentLookupKey(),该方法返回需要使用的DataSource的key值,然后根据这个key从resolvedDataSources这个map里取出对应的DataSource,如果找不到则用默认的resolvedDefaultDataSource。

详细实现过程
修改spring的配置文件
配置文件里需要配置多个数据源,改造后主要配置如下:

<bean id="dataSourceTargetA" class="com.mchange.v2.c3p0.ComboPooledDataSource"
    destroy-method="close" scope="singleton">
    <property name="driverClass" value="${jdbc.a.driverClassName}" />
    <property name="jdbcUrl" value="${jdbc.a.url}" />
    <property name="user" value="${jdbc.a.username}" />
    <property name="password" value="${jdbc.a.password}" />
    <!--当连接池中的连接耗尽的时候c3p0一次同时获取的连接数。Default: 3  -->
    <property name="acquireIncrement" value="${c3p0.acquireIncrement}"></property>
    <!-- 初始化时获取三个连接,取值应在minPoolSize与maxPoolSize之间。Default: 3 -->
    <property name="initialPoolSize" value="${c3p0.initialPoolSize}"></property>
    <!--最大空闲时间,60秒内未使用则连接被丢弃。若为0则永不丢弃。Default: 0 -->
    <property name="maxIdleTime" value="${c3p0.maxIdleTime}"></property>
    <!--连接池中保留的最大连接数。Default: 15 -->
    <property name="maxPoolSize" value="${c3p0.maxPoolSize}"></property>
    <property name="minPoolSize" value="${c3p0.minPoolSize}"></property>
    <!--两次连接中间隔时间,单位毫秒。Default: 1000 -->
    <property name="acquireRetryDelay" value="1000"></property>
    <!--定义在从数据库获取新连接失败后重复尝试的次数。Default: 30 -->
    <property name="acquireRetryAttempts" value="60"></property>
    <!--获取连接失败将会引起所有等待连接池来获取连接的线程抛出异常。但是数据源仍有效保留,并在下次调用getConnection()的时候继续尝试获取连接。如果设为true,那么在尝试获取连接失败后该数据源将申明已断开并永久关闭。Default:false -->
    <property name="breakAfterAcquireFailure" value="false"></property>
</bean>

<bean id="dataSourceTargetB" class="com.mchange.v2.c3p0.ComboPooledDataSource"
      destroy-method="close" scope="singleton">
    <property name="driverClass" value="${jdbc.b.driverClassName}" />
    <property name="jdbcUrl" value="${jdbc.b.url}" />
    <property name="user" value="${jdbc.b.username}" />
    <property name="password" value="${jdbc.b.password}" />
    <!--当连接池中的连接耗尽的时候c3p0一次同时获取的连接数。Default: 3 -->
    <property name="acquireIncrement" value="${c3p0.acquireIncrement}"></property>
    <!--初始化时获取三个连接,取值应在minPoolSize与maxPoolSize之间。Default: 3 -->
    <property name="initialPoolSize" value="${c3p0.initialPoolSize}"></property>
    <!--最大空闲时间,60秒内未使用则连接被丢弃。若为0则永不丢弃。Default: 0 -->
    <property name="maxIdleTime" value="${c3p0.maxIdleTime}"></property>
    <!--连接池中保留的最大连接数。Default: 15 -->
    <property name="maxPoolSize" value="${c3p0.maxPoolSize}"></property>
    <property name="minPoolSize" value="${c3p0.minPoolSize}"></property>
    <!--两次连接中间隔时间,单位毫秒。Default: 1000 -->
    <property name="acquireRetryDelay" value="1000"></property>
    <!--定义在从数据库获取新连接失败后重复尝试的次数。Default: 30 -->
    <property name="acquireRetryAttempts" value="60"></property>
    <!--获取连接失败将会引起所有等待连接池来获取连接的线程抛出异常。但是数据源仍有效保留,并在下次调用getConnection()的时候继续尝试获取连接。如果设为true,那么在尝试获取连接失败后该数据源将申明已断开并永久关闭。Default:
        false -->
    <property name="breakAfterAcquireFailure" value="false"></property>
</bean>
<!-- 动态数据源 -->
<bean id="dynamicDataSource" class="com.test.util.DynamicDataSource" >
    <property name="targetDataSources">
        <map key-type="java.lang.String">
            <entry value-ref="dataSourceTargetA" key="dataSourceTargetA"></entry>
            <entry value-ref="dataSourceTargetB" key="dataSourceTargetB"></entry>
        </map>
    </property>
    <property name="defaultTargetDataSource" ref="dataSourceTargetA" >
    </property>
</bean>


<bean id="txManager"
    class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dynamicDataSource" />
</bean>


<!-- MyBatis ORM operation class -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="dataSource" ref="dynamicDataSource" />
    <property name="mapperLocations">
        <list>
            <value>classpath*:com/test/**/*Mapper.xml</value>
        </list>
    </property>
    <property name="configLocation" value="classpath:provider-sql-map-config.xml" />
</bean>

复制代码

2.添加动态数据源管理类

DynamicDataSource类继承AbstractRoutingDataSource,实现determineCurrentLookupKey()方法。

package com.test.util;
import com.test.util.CustomerContextHolder;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

/**

  • 动态数据源管理类

*/
public class DynamicDataSource extends AbstractRoutingDataSource {

@Override
protected Object determineCurrentLookupKey() {
    return CustomerContextHolder.getCustomerType();
}

}
复制代码
3. 数据源切换类

CustomerContextHolder类可以实现数据源A和B的切换,数据源标识保存在线程变量中,避免多线程操作数据源时互相干扰。

package com.test.util;
/**

  • 数据源切换类

*/
public class CustomerContextHolder {
public static final String DATA_SOURCE_DEFAULT = “dataSourceTargetA”;
public static final String DATA_SOURCE_B = “dataSourceTargetB”;
private static final ThreadLocal contextHolder = new ThreadLocal();
public static void setCustomerType(String customerType) {
contextHolder.set(customerType);
}
public static String getCustomerType() {
return contextHolder.get();
}
public static void clearCustomerType() {
contextHolder.remove();
}
}

  1. 使用多数据源代码

下面是Controller层代码,需要查询B数据源,查询完成后切换为默认数据源A,具体代码如下:

复制代码
/**
* 订单列表查询,查询B数据源
*
* @param request
* @return
*/
@RequestMapping(value = “/list”)
public String list(HttpServletRequest request, Integer cId) {
//设置数据源B
CustomerContextHolder.setCustomerType(CustomerContextHolder.DATA_SOURCE_B);
List orderList = orderService.getListByCid(cId);
request.setAttribute(“orderList”, orderList);
//切换回默认数据源A
CustomerContextHolder.setCustomerType(CustomerContextHolder.DATA_SOURCE_DEFAULT);
return “/jsp/order”;
}

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!