SpringMVC Mybatis配置多个数据源并切换代码详解
作者:Java碎碎念 发布时间:2023-10-30 15:33:43
这篇文章主要介绍了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配置多个数据源并切换,有问题欢迎留言沟通哦!
来源:https://www.cnblogs.com/haha12/p/11926361.html


猜你喜欢
- 基本用法不说了,网上例子很多,这里主要介绍下比较特殊情况下使用的方法。1. 分组有的时候,我们对一个实体类需要有多中验证方式,在不同的情况下
- 前言Exchange(交换器)顾名思义,它是用来实现两个线程间的数据交换的,它诞生于 JDK 1.5,它有两个核心方法:exchange(V
- 邮件发送 方法一:使用System.Web.Mail命名空间(此方法我测试没有成功过) #region 发送邮件:此方法失败 pr
- 本文实例为大家分享了Java实现医院管理系统的具体代码,供大家参考,具体内容如下1.开发工具NetBeans8.2Mysql5.7mysql
- AlertDialog可以在当前的界面上显示一个对话框,这个对话框是置顶于所有界面元素之上的,能够屏蔽掉其他控件的交互能力,因此AlertD
- 一.背景最近业务需求需要导出Execl,最终做出的效果如下,中间牵扯到大量的数据计算。二.疑难问题分析问题1:跨单元格处理及边框设置问题2:
- 实例如下:public class CustomScrollView extends ScrollView {private Gesture
- 本文实例为大家分享了java实现购物车功能的具体代码,供大家参考,具体内容如下1 需要实现1、实现淘淘商城的购物车功能2 购物车功能2.1
- 1:先检查 字段有没有加上注解 @TableField(fill = FieldFill.INSERT_UPDATE)@TableField
- 如下所示:public static String reThreeStr(String ss){boolean result= ss.mat
- 目录进程、线程1. 进程2. 线程分时、分片同步、异步异步、多线程异步多线程效率多线程无序性扩展异步多线程版本下一篇:C# 异步多线程入门到
- 现在很多电脑提供了蓝牙支持,很多笔记本网卡也集成了蓝牙功能,也可以采用USB蓝牙方便的连接手机等蓝牙设备进行通信。操作蓝牙要使用类库InTh
- 前言本问主要介绍DataBinding在Android App中的使用方法。数据绑定是将“提供器”的数据源与“消费者”绑定并使其同步的一种通
- 判断用户输入的是否至少含有N位小数。1.当用户输入的是非数字时抛出异常,返回false。2.当用户输入数字是,判断其数字是否至少含有N位小数
- Class类中获取方法:public Method[] getMethods();//获取包括自身和继承(实现)过来的所有的public方法
- 定义栈又名堆栈,是一种操作受限的线性表,仅能在表尾进行插入和删除操作。它的特点是先进后出,就好比我们往桶里面放盘子,放的时候都是从下往上一个
- 锁是个非常有用的工具,运用场景非常多,因为它使用起来非常简单,而且易于理解。但同时它也会带来一些困扰,那就是可能会引起死锁,一旦产生死锁,就
- springboot:接收date类型的参数今天有个postmapping方法,地址都正确,就是死活进不去,真是奇怪了。终于从日志中得出些端
- 线程堆栈:简称栈 Stack托管堆: 简称堆 Heap使用.Net框架开发程序的时候,我们无需关心内存分配问题,因为有GC这个大管家给我们料
- 本文以一个简单的实例为大家介绍Android编程的入门知识,该案例是属于较早期的实例程序,读者可以对比学习,全面的了解Android程序的演