mybatis-plus动态数据源,多线程内不生效问题
mybatis-plus动态数据源,多线程内不生效问题前提:mybatis-plus + dynamic-datasource-spring-boot-starter问题描述解决方法代码示例前提:mybatis-plus + dynamic-datasource-spring-boot-starter此问题出现前需要你已完成mybatis-plus和dynamic-datasource-sprin
·
mybatis-plus动态数据源,多线程内不生效问题
前提:mybatis-plus + dynamic-datasource-spring-boot-starter
此问题出现前需要你已完成mybatis-plus和dynamic-datasource-spring-boot-starter实现动态数据源切换
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.3.1.tmp</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
<version>3.1.0</version>
</dependency>
问题描述
使用@DS配置动态数据源后,方法调用在CompletableFuture中异步执行程序时,异步程序中的数据源一直使用的默认数据源,并没有根据@DS注解中的配置切换
解决方法
参考dynamic-datasource-spring-boot-starter中的 DynamicDataSourceContextHolder 类
/**
* Copyright © 2018 organization baomidou
* <pre>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* <pre/>
*/
package com.baomidou.dynamic.datasource.toolkit;
import java.util.ArrayDeque;
import java.util.Deque;
import org.springframework.core.NamedInheritableThreadLocal;
import org.springframework.util.StringUtils;
/**
* 核心基于ThreadLocal的切换数据源工具类
*
* @author TaoYu Kanyuxia
* @since 1.0.0
*/
public final class DynamicDataSourceContextHolder {
/**
* 为什么要用链表存储(准确的是栈)
* <pre>
* 为了支持嵌套切换,如ABC三个service都是不同的数据源
* 其中A的某个业务要调B的方法,B的方法需要调用C的方法。一级一级调用切换,形成了链。
* 传统的只设置当前线程的方式不能满足此业务需求,必须模拟栈,后进先出。
* </pre>
*/
@SuppressWarnings("unchecked")
private static final ThreadLocal<Deque<String>> LOOKUP_KEY_HOLDER = new NamedInheritableThreadLocal("dynamic-datasource") {
@Override
protected Object initialValue() {
return new ArrayDeque();
}
};
private DynamicDataSourceContextHolder() {
}
/**
* 获得当前线程数据源
*
* @return 数据源名称
*/
public static String peek() {
return LOOKUP_KEY_HOLDER.get().peek();
}
/**
* 设置当前线程数据源
* <p>
* 如非必要不要手动调用,调用后确保最终清除
* </p>
*
* @param ds 数据源名称
*/
public static void push(String ds) {
LOOKUP_KEY_HOLDER.get().push(StringUtils.isEmpty(ds) ? "" : ds);
}
/**
* 清空当前线程数据源
* <p>
* 如果当前线程是连续切换数据源 只会移除掉当前线程的数据源名称
* </p>
*/
public static void poll() {
Deque<String> deque = LOOKUP_KEY_HOLDER.get();
deque.poll();
if (deque.isEmpty()) {
LOOKUP_KEY_HOLDER.remove();
}
}
/**
* 强制清空本地线程
* <p>
* 防止内存泄漏,如手动调用了push可调用此方法确保清除
* </p>
*/
public static void clear() {
LOOKUP_KEY_HOLDER.remove();
}
}
代码示例
@Service
@DS("#request.region") //这里使用的参数内的字段(提取出基础字段,所有参数类继承基础字段类)
public class ResServiceImpl {
@Autowired
private TestParamService testParamService;
public void test(TestParamRequest request) {
//异步执行(jdk8)
CompletableFuture.runAsync(() -> {
//设置当前线程数据源 request.getDcName()获取的是配置文件中配置的数据源名,如master,slave
DynamicDataSourceContextHolder.push(request.getDcName());
//TODO ...
testParamService.add(request);
//强制清空本地线程,防止内存泄漏,如手动调用了push可调用此方法确保清除
DynamicDataSourceContextHolder.clear();
});
}
}

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐
所有评论(0)