SpringMVC Mybatis配置多个数据源并切换代码详解

 更新时间:2019年11月26日 15:20:47   作者:Java碎碎念  
这篇文章主要介绍了SpringMVC Mybatis配置多个数据源并切换代码详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下

这篇文章主要介绍了SpringMVC Mybatis配置多个数据源并切换代码详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下

最近公司一个项目需要连接两个数据库(A和B)操作,有的模块查询A库,有的模块查询B库,因此需要改造下,项目后台用的是SpringMVC+Mybatis+MySQL架构,折腾了两天后终于搞定了,在这里记录过改造过程。

使用场景

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

  • 主从数据库切换
  • 读写分离
  • 兼容旧库

实现原理

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>

添加动态数据源管理类

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();
  }
}

数据源切换类

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<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();
  }
}

使用多数据源代码

下面是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<Order> orderList = salaryBillService.getSalaryOrderByOrgId(cId);
    request.setAttribute("orderList", orderList);
    //切换回默认数据源A
    CustomerContextHolder.setCustomerType(CustomerContextHolder.DATA_SOURCE_DEFAULT);
    return "/jsp/order";
  }

上述四步操作完成后即可实现SpringMVC+Mybatis配置多个数据源并切换,有问题欢迎留言沟通哦!

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

相关文章

  • SpringBoot 接口开发教程(httpclient客户端)

    SpringBoot 接口开发教程(httpclient客户端)

    这篇文章主要介绍了SpringBoot 接口开发教程(httpclient客户端),具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-03-03
  • Java中正则表达式的语法以及matches方法的使用方法

    Java中正则表达式的语法以及matches方法的使用方法

    正则表达式(Regular Expression)是一门简单语言的语法规范,是强大、便捷、高效的文本处理工具,这篇文章主要给大家介绍了关于Java中正则表达式的语法以及matches方法的使用方法,文中通过代码介绍的非常详细,需要的朋友可以参考下
    2024-05-05
  • Java语言简介(动力节点Java学院整理)

    Java语言简介(动力节点Java学院整理)

    Java是一门面向对象编程语言,不仅吸收了C++语言的各种优点,还摒弃了C++里难以理解的多继承、指针等概念,因此Java语言具有功能强大和简单易用两个特征,下面通过本文给大家分享java语言的简介,感兴趣的朋友一起看看吧
    2017-03-03
  • 深入理解final变量的初始化

    深入理解final变量的初始化

    本篇文章是对final变量的初始化进行了详细的分析介绍,需要的朋友参考下
    2013-06-06
  • maven打包zip包含bin下启动脚本的完整代码

    maven打包zip包含bin下启动脚本的完整代码

    这篇文章主要介绍了maven打包zip包含bin下启动脚本,本文给大家讲解的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-10-10
  • spring boot @PathVariable传递带反斜杠参数 / 的处理

    spring boot @PathVariable传递带反斜杠参数 / 的处理

    这篇文章主要介绍了spring boot @PathVariable传递带反斜杠参数 / 的处理操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2021-02-02
  • Springboot内嵌SQLite配置使用详解

    Springboot内嵌SQLite配置使用详解

    这篇文章主要介绍了Springboot内嵌SQLite配置使用详解,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-08-08
  • Java设计模式之工厂模式实现方法详解

    Java设计模式之工厂模式实现方法详解

    这篇文章主要介绍了Java设计模式之工厂模式实现方法,结合实例形式较为详细的分析了工厂模式的分类、原理、实现方法与相关注意事项,需要的朋友可以参考下
    2017-12-12
  • 基于SpringBoot服务端表单数据校验的实现方式

    基于SpringBoot服务端表单数据校验的实现方式

    这篇文章主要介绍了基于SpringBoot服务端表单数据校验的实现方式,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-10-10
  • mybatis-sqlserver批量新增返回id方式

    mybatis-sqlserver批量新增返回id方式

    这篇文章主要介绍了mybatis-sqlserver批量新增返回id方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-05-05

最新评论