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…...
C++_核心编程_多态案例二-制作饮品
#include <iostream> #include <string> using namespace std;/*制作饮品的大致流程为:煮水 - 冲泡 - 倒入杯中 - 加入辅料 利用多态技术实现本案例,提供抽象制作饮品基类,提供子类制作咖啡和茶叶*//*基类*/ class AbstractDr…...
蓝牙 BLE 扫描面试题大全(2):进阶面试题与实战演练
前文覆盖了 BLE 扫描的基础概念与经典问题蓝牙 BLE 扫描面试题大全(1):从基础到实战的深度解析-CSDN博客,但实际面试中,企业更关注候选人对复杂场景的应对能力(如多设备并发扫描、低功耗与高发现率的平衡)和前沿技术的…...
Springcloud:Eureka 高可用集群搭建实战(服务注册与发现的底层原理与避坑指南)
引言:为什么 Eureka 依然是存量系统的核心? 尽管 Nacos 等新注册中心崛起,但金融、电力等保守行业仍有大量系统运行在 Eureka 上。理解其高可用设计与自我保护机制,是保障分布式系统稳定的必修课。本文将手把手带你搭建生产级 Eur…...
土地利用/土地覆盖遥感解译与基于CLUE模型未来变化情景预测;从基础到高级,涵盖ArcGIS数据处理、ENVI遥感解译与CLUE模型情景模拟等
🔍 土地利用/土地覆盖数据是生态、环境和气象等诸多领域模型的关键输入参数。通过遥感影像解译技术,可以精准获取历史或当前任何一个区域的土地利用/土地覆盖情况。这些数据不仅能够用于评估区域生态环境的变化趋势,还能有效评价重大生态工程…...
IT供电系统绝缘监测及故障定位解决方案
随着新能源的快速发展,光伏电站、储能系统及充电设备已广泛应用于现代能源网络。在光伏领域,IT供电系统凭借其持续供电性好、安全性高等优势成为光伏首选,但在长期运行中,例如老化、潮湿、隐裂、机械损伤等问题会影响光伏板绝缘层…...
如何在最短时间内提升打ctf(web)的水平?
刚刚刷完2遍 bugku 的 web 题,前来答题。 每个人对刷题理解是不同,有的人是看了writeup就等于刷了,有的人是收藏了writeup就等于刷了,有的人是跟着writeup做了一遍就等于刷了,还有的人是独立思考做了一遍就等于刷了。…...
什么是Ansible Jinja2
理解 Ansible Jinja2 模板 Ansible 是一款功能强大的开源自动化工具,可让您无缝地管理和配置系统。Ansible 的一大亮点是它使用 Jinja2 模板,允许您根据变量数据动态生成文件、配置设置和脚本。本文将向您介绍 Ansible 中的 Jinja2 模板,并通…...
springboot整合VUE之在线教育管理系统简介
可以学习到的技能 学会常用技术栈的使用 独立开发项目 学会前端的开发流程 学会后端的开发流程 学会数据库的设计 学会前后端接口调用方式 学会多模块之间的关联 学会数据的处理 适用人群 在校学生,小白用户,想学习知识的 有点基础,想要通过项…...
GruntJS-前端自动化任务运行器从入门到实战
Grunt 完全指南:从入门到实战 一、Grunt 是什么? Grunt是一个基于 Node.js 的前端自动化任务运行器,主要用于自动化执行项目开发中重复性高的任务,例如文件压缩、代码编译、语法检查、单元测试、文件合并等。通过配置简洁的任务…...
LLMs 系列实操科普(1)
写在前面: 本期内容我们继续 Andrej Karpathy 的《How I use LLMs》讲座内容,原视频时长 ~130 分钟,以实操演示主流的一些 LLMs 的使用,由于涉及到实操,实际上并不适合以文字整理,但还是决定尽量整理一份笔…...
