mybatis plus 配置多数据源(数据源进行切换)
多数据源(数据源进行切换)
AbstractRoutingDataSource 根据用户定义的规则选择当前的数据源,这样我们可以在执行查询之前,设置使用的数据源。实现可动态路由的数据源,在每次数据库查询操作前执行。它的抽象方法 determineCurrentLookupKey() 决定使用哪个数据源。
1、application.yml中配置多个数据源
# Order
spring.datasource.order.url=jdbc:mysql://localhost:3306/seata_order?useUnicode=true&characterEncoding=utf8&allowMultiQueries=true&useSSL=false
spring.datasource.order.username=root
spring.datasource.order.password=123456
spring.datasource.order.driver-class-name=com.mysql.cj.jdbc.Driver
# Storage
spring.datasource.storage.url=jdbc:mysql://localhost:3306/seata_storage?useUnicode=true&characterEncoding=utf8&allowMultiQueries=true&useSSL=false
spring.datasource.storage.username=root
spring.datasource.storage.password=123456
spring.datasource.storage.driver-class-name=com.mysql.cj.jdbc.Driver
# Pay
spring.datasource.pay.url=jdbc:mysql://localhost:3306/seata_pay?useUnicode=true&characterEncoding=utf8&allowMultiQueries=true&useSSL=false
spring.datasource.pay.username=root
spring.datasource.pay.password=123456
spring.datasource.pay.driver-class-name=com.mysql.cj.jdbc.Driver
2、主启动类添加注解
@SpringBootApplication
@MapperScan("com.example.demo.mapper")
3、编写配置类
@Getter
public enum DataSourceKey {
/**
* Order data source key.
*/
ORDER,
/**
* Storage data source key.
*/
STORAGE,
/**
* Pay data source key.
*/
PAY,
}
public class DynamicDataSourceContextHolder {
private static final ThreadLocal<String> CONTEXT_HOLDER = ThreadLocal.withInitial(DataSourceKey.ORDER::name);
private static List<Object> dataSourceKeys = new ArrayList<>();
public static void setDataSourceKey(DataSourceKey key) {
CONTEXT_HOLDER.set(key.name());
}
public static String getDataSourceKey() {
return CONTEXT_HOLDER.get();
}
public static void clearDataSourceKey() {
CONTEXT_HOLDER.remove();
}
public static List<Object> getDataSourceKeys() {
return dataSourceKeys;
}
}
public class DynamicRoutingDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
//log.info("当前数据源 [{}]", DynamicDataSourceContextHolder.getDataSourceKey());
return DynamicDataSourceContextHolder.getDataSourceKey();
}
}
@Configuration
public class DataSourceProxyConfig {
@Bean("originOrder")
@ConfigurationProperties(prefix = "spring.datasource.order")
public DataSource dataSourceMaster() {
return new DruidDataSource();
}
@Bean("originStorage")
@ConfigurationProperties(prefix = "spring.datasource.storage")
public DataSource dataSourceStorage() {
return new DruidDataSource();
}
@Bean("originPay")
@ConfigurationProperties(prefix = "spring.datasource.pay")
public DataSource dataSourcePay() {
return new DruidDataSource();
}
@Bean(name = "order")
public DataSourceProxy masterDataSourceProxy(@Qualifier("originOrder") DataSource dataSource) {
return new DataSourceProxy(dataSource);
}
@Bean(name = "storage")
public DataSourceProxy storageDataSourceProxy(@Qualifier("originStorage") DataSource dataSource) {
return new DataSourceProxy(dataSource);
}
@Bean(name = "pay")
public DataSourceProxy payDataSourceProxy(@Qualifier("originPay") DataSource dataSource) {
return new DataSourceProxy(dataSource);
}
@Bean("dynamicDataSource")
public DataSource dynamicDataSource(@Qualifier("order") DataSource dataSourceOrder,
@Qualifier("storage") DataSource dataSourceStorage,
@Qualifier("pay") DataSource dataSourcePay) {
DynamicRoutingDataSource dynamicRoutingDataSource = new DynamicRoutingDataSource();
Map<Object, Object> dataSourceMap = new HashMap<>(3);
dataSourceMap.put(DataSourceKey.ORDER.name(), dataSourceOrder);
dataSourceMap.put(DataSourceKey.STORAGE.name(), dataSourceStorage);
dataSourceMap.put(DataSourceKey.PAY.name(), dataSourcePay);
dynamicRoutingDataSource.setDefaultTargetDataSource(dataSourceOrder);
dynamicRoutingDataSource.setTargetDataSources(dataSourceMap);
DynamicDataSourceContextHolder.getDataSourceKeys().addAll(dataSourceMap.keySet());
return dynamicRoutingDataSource;
}
@Bean
@ConfigurationProperties(prefix = "mybatis-plus") // MybatisSqlSessionFactoryBean中有各种MybatisPlus的配置属性(globalConfig、mapperLocations} 而SqlSessionFactoryBean中则是mybatis的各种配置属性(typeAlies、mapperLocations)
public MybatisSqlSessionFactoryBean sqlSessionFactoryBean(@Qualifier("dynamicDataSource") DataSource dataSource) {
// 这里用 MybatisSqlSessionFactoryBean 代替了 SqlSessionFactoryBean,否则 MyBatisPlus 不会生效
MybatisSqlSessionFactoryBean mybatisSqlSessionFactoryBean = new MybatisSqlSessionFactoryBean();
mybatisSqlSessionFactoryBean.setDataSource(dataSource);
return mybatisSqlSessionFactoryBean;
}
}
调用切换数据源:
@GlobalTransactional
@Override
public OperationResponse placeOrder(PlaceOrderRequestVO placeOrderRequestVO) throws Exception {
DynamicDataSourceContextHolder.setDataSourceKey(DataSourceKey.ORDER);//切换数据源
Integer amount = 1;
Integer price = placeOrderRequestVO.getPrice();
Order order = Order.builder().build();
Integer saveOrderRecord = orderDao.insert(order);
// 扣减库存
boolean operationStorageResult = storageService.reduceStock(placeOrderRequestVO.getProductId(), amount);
// 扣减余额
boolean operationBalanceResult = payService.reduceBalance(placeOrderRequestVO.getUserId(), price);
DynamicDataSourceContextHolder.setDataSourceKey(DataSourceKey.ORDER);//切换数据源
order.setStatus(OrderStatus.SUCCESS);
Integer updateOrderRecord = orderDao.updateById(order);
return success(operationStorageResult && operationBalanceResult);
}
项目启动报错:Failed to configure a DataSource: 'url' attribute is not specified and no embedded datasource could be configured
原因:导入spring-mybatis依赖后,springboot启动时会自动加载数据源,由于dataSource配置成多数据源加载不到spring.datasource.url故而报错。
解决:1、主启动类添加@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
2、若上面配置还是无法解决,可以配置一个默认数据源让其启动时加载(不影响,会被多数据源切换时覆盖的):spring.datasource.url
额外:
SqlSessionFactory factory=new SqlSessionFactoryBuilder().build(in);
SqlSession sqlSession=factory.openSession(); //sqlSession就是用来操作sql语句的
使用 MyBatis-Spring 之后, 会使用SqlSessionFactoryBean来代替SqlSessionFactoryBuilder创建SqlSessionFactory
MybatisPlus需要使用MybatisSqlSessionFactoryBean。
相关文章:

mybatis plus 配置多数据源(数据源进行切换)
多数据源(数据源进行切换) AbstractRoutingDataSource 根据用户定义的规则选择当前的数据源,这样我们可以在执行查询之前,设置使用的数据源。实现可动态路由的数据源,在每次数据库查询操作前执行。它的抽象方法 determineCurrentLookupKey()…...

Docker-Android安卓模拟器本地部署并实现远程开发测试
文章目录 1. 虚拟化环境检查2. Android 模拟器部署3. Ubuntu安装Cpolar4. 配置公网地址5. 远程访问小结 6. 固定Cpolar公网地址7. 固定地址访问 本文主要介绍如何在Ubuntu系统使用Docker部署docker-android安卓模拟器,并结合cpolar内网穿透工具实现公网远程访问本地…...

vue-封装上下(垂直方向)轮播
需求:没有找到特别好的上下轮播插件-所以自己写了一个简单的demo 一.上下平滑轮播-移入停止-移出继续轮播 <!--* 消息滚动 --> <template><div class"news"><div id"roll" class"InfoBox" mouseover"thi…...

海外私人IP和原生IP有什么区别,谁更有优势?
一、什么是海外私人IP?什么是原生IP? 1、海外私人IP: 海外私人IP是由专门的服务提供商提供的IP地址,这些IP地址通常与特定地理位置或国家相关联。这些IP地址独享私人而不用与其他用户共享。海外私人IP广泛应用与跨境电商中&#x…...

主流容器工具对比以及重点推荐学习的企业级工具
常见的主流容器工具包括但不限于以下几种: 1. Docker: Docker 是最流行的容器平台之一,它允许开发者将应用及其依赖打包到一个轻量级、可移植的容器中,然后可以在任何支持Docker的系统上运行。 2. Kubernetes:Kubern…...

国产linux系统(银河麒麟,统信uos)使用 PageOffice 国产版在线编辑word文件,同时保存数据和文件
PageOffice 国产版 :支持信创系统,支持银河麒麟V10和统信UOS,支持X86(intel、兆芯、海光等)、ARM(飞腾、鲲鹏、麒麟等)芯片架构。 在OA办公、文档流转等各个Web系统中,实现最简单的…...

个人感觉对Material设计有用的几个网址
(一) Modular and customizable Material Design UI components for Android GIthub: material-components-android (二) 学习Material设计 Material Design (三) 用于创建Material主题,支持导出多种格式 material-theme-builder...

ubuntu18 安装sudo
ubuntu18 安装sudo 在Ubuntu 18.04上安装sudo通常是不必要的,因为sudo是Ubuntu及其衍生版本的基本包之一,默认情况下就已经安装。如果出于某种原因,sudo没有预装或者你需要升级到最新版本,你可以通过以下命令安装或更新它&#x…...

【力扣一轮】202.快乐数 1.两数之和
202.快乐数 力扣链接 代码随想录链接 思路 看到这一题没思路,直接看题解。 发现其中一个难点在于“无限循环”,这个字眼可以转换成退出条件。退出条件就有两种,一种是这个数字是快乐数,一种是这个数字不是快乐数。 如果是快…...

Vue小程序项目知识积累(二)
1.wx.reLaunch(Object object) 关闭所有页面,打开到应用内的某个页面。 wx.reLaunch({url:/pages/positons/index}) 参数说明: 属性类型默认值必填说明urlstring是需要跳转的应用内页面路径 (代码包路径),路径后可以带参数。参数与路径之…...

RK3588 Android13 预安装自己的apk应用及把这个应用设置为默认桌面
1、cp -rf device/rockchip/rk3588/rk3588m_s/preinstall device/rockchip/rk3588/rk3588_t/ 2、给device/rockchip/rk3588/rk3588_t/preinstall/的存放app的文件夹改名为app-imms2,在app-imms2放入app-imms2.apk,编译安卓源码即可, 3、编译完会在out/…...

NLP(16)--生成式任务
前言 仅记录学习过程,有问题欢迎讨论 输入输出均为不定长序列(seq2seq)自回归语言模型: x 为 str[start : end ]; y为 [start1 : end 1] 同时训练多个字,逐字计算交叉熵 encode-decode结构: Encoder将输…...

直播回放| 机器人任务挑战赛线上培训资料合集
大赛培训回顾 5月22日,卓翼飞思实验室为全国各赛区精心组织的机器人任务挑战赛(无人协同系统)线上培训第三期顺利落下帷幕,吸引300余人参与。本次培训主要针对仿真平台的基本使用,从仿真平台获取激光雷达/视觉数据&am…...

flask Web应用的接口调试
以上上一篇 Docker部署Azure chatgpt样例应用_群晖部署chatgpt-CSDN博客 xx为例 在app.py最下方有 /conversation 接口 在api.ts文件中可见调用了 /conversation 接口。 使用chrom浏览器F12查看 Networ- 本地运行后,使用postman调试。接口地址填写 http://127.0…...

简单易懂的 API 集成测试方法
简介:API 集成测试的重要性 API 集成测试是一类测试活动,用于验证 API 是否满足功能性、可靠性、性能和安全性等方面的预期要求。在多 API 协作的应用程序中,这种测试尤为紧要。 在这一阶段,我们不仅审视单个组件,还…...

leetcode 239. 滑动窗口最大值、347.前 K 个高频元素
leetcode 239. 滑动窗口最大值、347.前 K 个高频元素 leecode 239. 滑动窗口最大值 题目链接 :https://leetcode.cn/problems/sliding-window-maximum/description/ 题目 给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的…...

npm常用指令
基础 命令:run 解释:运行脚本 示例:npm run dev 命令:list || ls 解释:查看依赖列表 示例:npm list || npm ls 命令:install || i 解释:安装依赖 示例:npm install ||…...

数字孪生技术在管理中有哪些实际应用?
随着科学技术的不断提高,数字孪生技术也在不断的从理论应用至现实,并且涉及领域较为广泛。 在生产运营管理层面,通过构建数字孪生模型,企业可以精准模拟和优化生产线,实现生产流程的智能化和高效化。比如,…...

LeetCode/NowCoder-链表经典算法OJ练习3
孜孜不倦:孜孜:勤勉,不懈怠。指工作或学习勤奋不知疲倦。💓💓💓 目录 说在前面 题目一:返回倒数第k个节点 题目二:链表的回文结构 题目三:相交链表 SUMUP结尾 说在前…...

如何理解HTML语义化
如何理解HTML语义化 HTML语义化,简单来说,就是使用HTML标签来清晰地表达页面内容的结构和意义,而不仅仅是作为布局的容器。它强调使用具有明确含义的HTML标签来描述页面元素,而不是仅仅依赖CSS来实现页面的外观和布局。 理解HTM…...

Solved problem: The number of elements in the character array
Problem: 未解决的问题:字符数组中元素的个数-CSDN博客 Solution: Add \0 at the end of the character array More detailed content can be found in the link below. Sizeof and Length of character array-CSDN博客...

Flume Channels简介及官方用例
通道是在代理上暂存事件的存储库。Source 添加事件,Sink 将其删除。 1、Memory Channel 事件存储在具有可配置最大大小的内存中队列中。它非常适合需要更高吞吐量的流,但在agent发生故障时会丢失暂存数据 Property Name Default Description type …...

【AI】如何用非Docker方法安装类GPT WebUI
【背景】 本地LLM通信的能力需要做成局域网SAAS服务才能方便所有人使用。所以需要安装WebUI,这样既有了用户界面,又做成了SAAS服务,很理想。 【问题】 文档基本首推都是Docker安装,虽然很多人都觉得容器多么多么方便࿰…...

2024年ai知识库:特点、应用与搭建
随着科技的进步和企业的需要,ai知识库逐渐走进大众的视野并深受企业的青睐,掀起了搭建ai知识库的热潮。LookLook同学就来简单介绍一下关于ai知识库的特点、应用与发展趋势,带你了解2024年的ai知识库。 一、ai知识库的定义与特点 ai知识库是结…...

查询一个字符串在另一个字符串中出现的次数(java)
查询一个字符串在另一个字符串中出现的次数 例: String str1“helloworld,java,python,hellokafka,world big table helloteacher”; String str2“hello”; 字符串str2在str1中出现3次 代码 package exercise.test8;public class Demo8 {public static void mai…...

Docker in Docker 原理与实战
一、引言 随着容器化技术的普及,Docker 作为一种主流的容器管理工具,已被广泛应用于开发、测试及生产环境中。Docker 的灵活性和便捷性使得它成为 DevOps 流程中不可或缺的一部分。然而,在一些复杂的应用场景中,我们可能需要在一…...

Rust学习心得
我分享一下一年的Rust学习经历,从书到代码都一网打尽。 关于新手如何学习Rust,我之前在Hacker News上看到了这么一篇教程: 这篇教程与其他教程不同的时,他不是一个速成教程,而是通过自己的学习经历,向需要…...

K8s deployment 进阶
文章目录 K8s deployment 进阶Deployment 更新策略RecreateRollingUpdatemaxSurge 和 maxUnavailable minReadySecondsprogressDeadlineSeconds Deployment 版本回滚Deployment 实现灰度发布 K8s deployment 进阶 Deployment 更新策略 Recreate 重建 (Recreate)&…...

python实现二叉搜索树(AVL树)简单样例
一、二叉搜索树 class TreeNode:def __init__(self, value):self.value valueself.left Noneself.right Noneclass BinarySearchTree:def __init__(self):self.root Nonedef insert(self, value):if self.root is None:self.root TreeNode(value)else:self._insert(self.…...

Day47 打家劫舍123
198 打家劫舍 题目链接:198.打家劫舍 你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,…...