MyBatis-Plus中动态数源切换
一、简单使用场景选择@DS注解指定
SpringBoot项目中如果不涉及多线程的情况下,需针对不同的实体类选择不同的数据源,可直接在impl实现类上添加@DS(“yml文件或properties文件中数据源名称”)来指定目标数源:
1. POM文件中引入多数源配置依赖及Druid数据库连接池依赖 :
<!-- 数据库连接池 1.2.5版本 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>${druid.version}</version>
</dependency>
<!-- 多数据源依赖 3.5.0版本 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
<version>${dynamic-datasource.version}</version>
</dependency>
2. 在指定impl实现类上添加@DS注解:
package org.xxx.xxx.impl;
import org.springframework.stereotype.Service;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.xxx.xxx.mapper.XxxMapper;
import org.xxx.xxx.pojo.frk.Xxx;
import org.xxx.xxxx.service.XxxService;
/**
* @Description: TODO
* @ClassName: XxxServiceImpl
* @Author: ZzHh
* @Date: 2023年09月14日 09:41
* @Version: 1.0
**/
@Service
@DS("localhost_test_mysql")
public class XxxServiceImpl extends ServiceImpl<XxxMapper, Xxx> implements XxxService {
}
以上步骤完成,即可在运行中通过配置的@DS注解获取指定数据源中对应表数据或操作指定数据源中对应的表。
二、多线程场景@DS注解失效解决方案
SpringBoot项目中若涉及到多线程的情况下,即便在impl实现类上添加上指定的@DS(“数据源名称”)注解,也不会有任何的效果,同时运行时会默认连接yml文件中或properties文件中配置的多数源的primary,若连接不通则直接报错,主要原因在于多线程的环境下,每个线程都有自己的线程栈和本地变量。
单线程切面会在方法调用前从ThreadLocal中获取数据源的名称,并根据名称切换数据源,但在多线程的环境下切面在切换数据源之前会先保存当前线程的数据源名称到ThreadLocal中,之后再从ThreadLocal中来获取新的数据源名称,从而切换数据源,但当切换数据源的方法在新启动的线程中被调用的时候,由于线程发生了切换,新线程的ThreadLocal会被重新初始化,即便在主线程中设置了数据源的名称,新线程的ThreadLocal仍然为空,就会导致@DS注解失效。
主要的解决方案有如下集中,此处采用手动设置push来完成:
①将线程切换和@DS注解放在同一个方法或类中,确保切换数据源的操作能在新线程中生效;
②在新线程中手动设置ThreadLocal中的数据源名称,让新线程中能正确获取到数据源的名称,并切换数据源;
③使用线程池,通过自定义线程工厂或任务的前置处理器等机制,在新线程被创建之前设置ThreadLocal中的数据源名称。
// 1.在实现Runnable方法创建多线程的类中重写run()方法
// 2.在重写的run()方法首行手动指定数据推送目标数据源名称
@Override
public void run() {
DynamicDataSourceContextHolder.push("yml文件或properties文件中配置的数据源名称");
...
}
以上一行代码即可解决@DS注解在多线程中失效的情况。