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

使用场景

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

  1. 主从数据库切换
  2. 读写分离
  3. 兼容旧库

实现原理

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

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

详细实现过程

  1. 修改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<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();
}
}

4. 使用多数据源代码

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

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

推荐阅读

1.Spring Boot 2.X 如何优雅的解决跨域问题?
2.Redis Cluster搭建高可用Redis服务器集群
3.为什么单线程的Redis这么快?
4.Spring Boot集成spring session实现session共享
5.Spring Boot入门-快速搭建web项目
6.Spring Boot2.0整合Redis
7.一篇文章搞定SpringMVC参数绑定

限时领取免费Java相关资料,涵盖了Java、Redis、MongoDB、MySQL、Zookeeper、Spring Cloud、Dubbo/Kafka、Hadoop、Hbase、Flink等高并发分布式、大数据、机器学习等技术。

资料传送门:  https://mp.weixin.qq.com/s/u2b_NVNuMuAPE0w4lc45fw

关注下方公众号即可免费领取:

SpringMVC+Mybatis 如何配置多个数据源并切换?的更多相关文章

  1. idea spring+springmvc+mybatis环境配置整合详解

    idea spring+springmvc+mybatis环境配置整合详解 1.配置整合前所需准备的环境: 1.1:jdk1.8 1.2:idea2017.1.5 1.3:Maven 3.5.2 2. ...

  2. SpringMVC+Mybatis+MySQL配置Redis缓存

    SpringMVC+Mybatis+MySQL配置Redis缓存 1.准备环境: SpringMVC:spring-framework-4.3.5.RELEASE-dist Mybatis:3.4.2 ...

  3. SSM Spring +SpringMVC+Mybatis 整合配置 及pom.xml

    SSM Spring +SpringMVC+Mybatis 配置 及pom.xml SSM框架(spring+springMVC+Mybatis) pom.xml文件 maven下的ssm整合配置步骤

  4. springMVC+Mybatis(使用AbstractRoutingDataSource实现多数据源切换时)事务管理未生效的解决办法

    业务场景: A.B两个单位,系统部署同一套代码: A.B两系统能相互访问: 要求将数据从A系统同步到B系统,再将反馈信息回发给A: 实际开发情况: 因为系统比较小,最开始设计架构的时候没有考虑到消息互 ...

  5. Springmvc + mybatis + spring 配置,spring事物

    今天配置了半天,发现,事物不起效果,主要出现如下错误: org.mybatis.spring.transaction.SpringManagedTransaction] - [JDBC Connect ...

  6. Spring+SpringMVC+MyBatis整合配置

    前端控制器 web.xml <?xml version="1.0" encoding="UTF-8"?> <web-app version=& ...

  7. spring+springMVC+Mybatis架构下采用AbstractRoutingDataSource、atomikos、JTA实现多数据源灵活切换以及分布式事务管理

    背景: 1.系统采用SSM架构.需要在10多个MYSQL数据库之间进行切换并对数据进行操作,上篇博文<springMVC+Mybatis(使用AbstractRoutingDataSource实 ...

  8. spring+springmvc+mybatis构建系统

    今天和大家分享的是spring+springmvc+mybatis搭建框架的例子,说到这里不得不说现在市面上一流大公司还有很多用这种架子,创业型公司大部分都用springboot集成的mvc+myba ...

  9. maven搭建springmvc+mybatis项目

    上一篇中已经成功使用maven搭建了一个web项目,本篇描述在此基础上怎么搭建一个基于springmvc+mybatis环境的项目. 说了这么久,为什么那么多人都喜欢用maven搭建项目?我们都知道m ...

随机推荐

  1. Jmeter利用正则表达式提取器提取登录cookie供下一步使用

    最近在学Jmeter,遇到需要登录之后才能进行下一步操作的场景,网上查了各位大神的资料,东拼西凑总算是做好满足需求了,写一下经过和步骤吧. 一.正常调用 按正常流程添加线程组.HTTP请求(登录和添加 ...

  2. python输出彩色字体

    Python终端如何输出彩色字体 实现过程:       终端的字符颜色是用转义序列控制的,是文本模式下的系统显示功能,和具体的语言无关.       转义序列是以ESC开头,即用\033来完成(ES ...

  3. 开发人员必备工具 —— JMeter 压测

    在接口开发完以后,开发人员应该学会对自己的接口先进行压测一下,虽然压测的结果并不一定准确,也不能完全反映真实情况,但是如果有问题的话多少是可以看出的,而且也可以及早做优化,做到心里有底.否则,等测试进 ...

  4. 使用强类型实体Id来避免原始类型困扰(一)

    原文地址:https://andrewlock.net/using-strongly-typed-entity-ids-to-avoid-primitive-obsession-part-1/ 作者: ...

  5. Python爬虫入门这一篇就够了

    何谓爬虫 所谓爬虫,就是按照一定的规则,自动的从网络中抓取信息的程序或者脚本.万维网就像一个巨大的蜘蛛网,我们的爬虫就是上面的一个蜘蛛,不断的去抓取我们需要的信息. 爬虫三要素 抓取 分析 存储 基础 ...

  6. TreeSet集合解析

    TreeSet是实现Set接口的实现类.所以它存储的值是唯一的,同时也可以对存储的值进行排序,排序用的是二叉树原理.所以要理解这个类,必须先简单理解一下什么是二叉树. 二叉树原理简析 假如有这么一个集 ...

  7. 浅谈CSS3 box-sizing 属性 有趣的盒模型

    盒模型的组成大家肯定都懂,由里向外content,padding,border,margin. 盒模型是有两种标准的,一个是标准模型,一个是IE模型. 从上面两图不难看出在标准模型中,盒模型的宽高只是 ...

  8. SuperMap iObject入门开发系列七管线横断面分析

    本文是一位好友“托马斯”授权给我来发表的,介绍都是他的研究成果,在此,非常感谢. 管线横断面分析功能是管线系统常见的一个分析功能,地下管线横断面分析的原理,是根据需求,在管线区域画一条横截面线,使其与 ...

  9. 《Python黑客编程之极速入门》正式开课

    玄魂 玄魂工作室 今天 之前开启了一个<Python黑客编程>的系列,后来中断了,内容当时设置的比较宽,不太适合入门.现在将其拆分成两个系列<Python黑客编程之极速入门>和 ...

  10. scrapy爬虫 快速入门

    Scrapy 1. 简介 Scrapy是一个为了爬取网站数据,提取结构性数据而编写的应用框架. 其可以应用在数据挖掘,信息处理或存储历史数据等一系列的程序中.其最初是为了页面抓取 (更确切来说, 网络 ...