基于SpringBoot+Druid实现多数据源:原生注解式
前言
本博客姊妹篇
- 基于SpringBoot+Druid实现多数据源:原生注解式
- 基于SpringBoot+Druid实现多数据源:注解+编程式
- 基于SpringBoot+Druid实现多数据源:baomidou多数据源
一、功能描述
- 配置方式:配置文件中实现多数据源,非动态
- 使用方式:使用注解切换数据源
二、代码实现
2.1 配置
# spring配置
spring:# 数据源配置datasource:type: com.alibaba.druid.pool.DruidDataSourcedruid:web-stat-filter:enabled: trueurl-pattern: /*exclusions: '*.js,*.css,*.gif,*.png,*.jpg,*.ico,/druid/*'stat-view-servlet:enabled: trueurl-pattern: /druid/*login-username: adminlogin-password: 123456filter:stat:enabled: truelog-slow-sql: trueslow-sql-millis: 1000merge-sql: truewall:enabled: trueconfig:multi-statement-allow: truemaster:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://127.0.0.1:3306/boot_business?useUnicode=true&characterEncoding=UTF8&serverTimezone=GMT%2B8&useSSL=falseusername: rootpassword: rootinitial-size: 10min-idle: 10max-active: 100max-wait: 60000time-between-eviction-runs-millis: 60000min-evictable-idle-time-millis: 300000validation-query: select 1test-while-idle: truetest-on-borrow: falsetest-on-return: falsepool-prepared-statements: truemax-pool-prepared-statement-per-connection-size: 20slave:enabled: truedriver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://127.0.0.1:3306/boot_codegen?useUnicode=true&characterEncoding=UTF8&serverTimezone=GMT%2B8&useSSL=falseusername: rootpassword: rootinitial-size: 10min-idle: 10max-active: 100max-wait: 60000time-between-eviction-runs-millis: 60000min-evictable-idle-time-millis: 300000validation-query: select 1test-while-idle: truetest-on-borrow: falsetest-on-return: falsepool-prepared-statements: truemax-pool-prepared-statement-per-connection-size: 20
2.2 配置类
package com.qiangesoft.datasource.core;import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;import java.util.HashMap;
import java.util.Map;/*** 多数据源配置** @author qiangesoft* @date 2024-03-14*/
@Slf4j
@Configuration
public class DataSourceConfiguration {@Bean@ConfigurationProperties("spring.datasource.druid.master")public DruidDataSource masterDataSource() {DruidDataSource masterDataSource = DruidDataSourceBuilder.create().build();masterDataSource.setName(DataSourceType.MASTER.getType());return masterDataSource;}@Bean@ConfigurationProperties("spring.datasource.druid.slave")@ConditionalOnProperty(prefix = "spring.datasource.druid.slave", name = "enabled", havingValue = "true")public DruidDataSource slaveDataSource() {DruidDataSource slaveDataSource = DruidDataSourceBuilder.create().build();slaveDataSource.setName(DataSourceType.SLAVE.getType());return slaveDataSource;}@Bean@Primarypublic DynamicDataSource dynamicDataSource(DruidDataSource masterDataSource, DruidDataSource slaveDataSource) {Map<Object, Object> targetDataSources = new HashMap<>();targetDataSources.put(masterDataSource.getName(), masterDataSource);targetDataSources.put(slaveDataSource.getName(), slaveDataSource);return new DynamicDataSource(masterDataSource, targetDataSources);}
}
2.3 多数据源扩展实现
package com.qiangesoft.datasource.core;import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;import javax.sql.DataSource;
import java.util.Map;/*** 动态数据源** @author qiangesoft* @date 2024-03-14*/
public class DynamicDataSource extends AbstractRoutingDataSource {public DynamicDataSource(DataSource defaultTargetDataSource, Map<Object, Object> targetDataSources) {super.setDefaultTargetDataSource(defaultTargetDataSource);super.setTargetDataSources(targetDataSources);super.afterPropertiesSet();}@Overrideprotected Object determineCurrentLookupKey() {return DataSourceContext.getDataSource();}
}
2.4 切面
package com.qiangesoft.datasource.core;import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;import java.util.Objects;/*** 多数据源处理** @author qiangesoft* @date 2024-03-14*/
@Slf4j
@Order(1)
@Aspect
@Component
public class DataSourceAspect {/*** 切点*/@Pointcut("@annotation(com.qiangesoft.datasource.core.DataSource)")public void pointCut() {}/*** 通知** @param joinPoint* @return* @throws Throwable*/@Around("pointCut()")public Object around(ProceedingJoinPoint joinPoint) throws Throwable {DataSource dataSource = this.getDataSource(joinPoint);if (dataSource == null) {DataSourceContext.setDataSource(DataSourceType.MASTER);} else {DataSourceContext.setDataSource(dataSource.value());}try {return joinPoint.proceed();} finally {DataSourceContext.removeDataSource();}}/*** 获取数据源** @param joinPoint* @return*/public DataSource getDataSource(ProceedingJoinPoint joinPoint) {MethodSignature signature = (MethodSignature) joinPoint.getSignature();// 方法上查找注解DataSource dataSource = AnnotationUtils.findAnnotation(signature.getMethod(), DataSource.class);if (Objects.nonNull(dataSource)) {return dataSource;}// 类上查找注解return AnnotationUtils.findAnnotation(signature.getDeclaringType(), DataSource.class);}
}
2.5 线程本地变量
package com.qiangesoft.datasource.core;/*** 数据源上下文** @author qiangesoft* @date 2024-03-14*/
public class DataSourceContext {/*** 线程本地变量:数据源*/private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>();/*** 设置数据源的变量*/public static void setDataSource(DataSourceType dataSourceType) {CONTEXT_HOLDER.set(dataSourceType.getType());}/*** 获得数据源的变量*/public static String getDataSource() {return CONTEXT_HOLDER.get();}/*** 清空数据源变量*/public static void removeDataSource() {CONTEXT_HOLDER.remove();}
}
2.6 使用
package com.qiangesoft.datasource.controller;import com.qiangesoft.datasource.core.DataSource;
import com.qiangesoft.datasource.core.DataSourceType;
import com.qiangesoft.datasource.entity.SysUser;
import com.qiangesoft.datasource.service.ISysUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import java.util.List;/*** <p>* 用户信息 前端控制器* </p>** @author qiangesoft* @since 2024-03-14*/
@RestController
@RequestMapping("/sys/user")
public class SysUserController {@Autowiredprivate ISysUserService sysUserService;@DataSource(DataSourceType.MASTER)@GetMapping("/master")public List<SysUser> listMaster() {return sysUserService.list();}@DataSource(DataSourceType.SLAVE)@GetMapping("/slave")public List<SysUser> listSlave() {return sysUserService.list();}}相关文章:
基于SpringBoot+Druid实现多数据源:原生注解式
前言 本博客姊妹篇 基于SpringBootDruid实现多数据源:原生注解式基于SpringBootDruid实现多数据源:注解编程式基于SpringBootDruid实现多数据源:baomidou多数据源 一、功能描述 配置方式:配置文件中实现多数据源,非…...
AJAX 03 XMLHttpRequest、Promise、封装简易版 axios
AJAX 学习 AJAX 3 原理01 XMLHttpRequest① XHR 定义② XHR & axios 关系③ 使用 XHR④ XHR查询参数案例:地区查询(URLSearchParams)⑤ XHR数据提交 POST 02 PromisePromise 使用Promise - 三种状态案例:使用Promise XHR 获取…...
如何将办公资料文件生成二维码?扫码可看详情
日常办公的时候,经常会需要应用二维码来向同事或者客户发送和展示一些资料。比如包含企业介绍和产品介绍的资料、一些操作流程的资料、产品展示宣传视频、活动安排详情、比赛流程、会议资料… 这些都能通过一个文件二维码来展示。 文件二维码支持将PDF文件生成二维…...
【Streamlit学习笔记】实现包含多个sheet的excel文件下载
1、什么是Streamlit Streamlit是一个免费的开源框架,用于快速构建和共享漂亮的机器学习和数据科学Web应用程序,官网链接 Streamlit Streamlit API链接 API reference 实际项目中遇到的问题:包含多个sheet的excel文件下载,下面将给…...
[Redis]——主从同步原理(全量同步、增量同步)
目录 Redis集群: 主从同步原理: replid和offset: 全量同步和增量同步: repl_baklog文件: 主从集群的优化: Redis集群: 部署多台Redis我们称之为Redis集群,他有一个主节点(负责写操作)&…...
Buildroot 之二 详解构建架构、流程、external tree、示例
构建系统 Buildroot 中的构建系统使用的是从 Linux Kernel(4.17-rc2) 中移植的 Kconfig(配置) + Makefile & Kbuild(编译)这套构建系统,移植后的源码位于 support/kconfig/ 目录下。Buildroot 本身是一个构建系统,与直接编译源码不同,因此,它对这套系统进行了比较…...
牛客小白月赛61-C-小喵觅食
很经典的bfs,就是从猫咪和MM的坐标开始bfs搜索 不过这题有些小细节需要注意 1.认真审题,注意,猫一旦闻到小鱼干的味道,开始动,此时MM就不动了,一开始没仔细审题,很不好的习惯 2.注意移动的条件,vis,不是墙,距离是MM的移动距离范围内 3.这个猫咪的r2是闻味道的r2,不是移动距…...
200 名专家编写报告:AI 发展可能对人类构成「灭绝级威胁」
3 月 14 日消息,美国国务院委托编写了一份新报告,警告 AI 正呈指数级发展,可能会对人类构成「灭绝级威胁」。 这份报告全称为《提高先进人工智能安全保障的行动计划》,要求美国政府必须迅速、果断地采取行动,以避免 A…...
学习Android的第二十九天
目录 Android Service 与 Activity 通讯 范例 Android Service Alarm 定时广播 Alarm Alarm 使用流程 范例 Android IBinder Binder 为什么是 Binder ? Android Service 与 Activity 通讯 Activity 与 Service 通信的媒介就是 Service 中的 onBind() 方法࿰…...
SpringMVC重点记录
目录 1.学习重点2.回顾MVC3.回顾servlet4.初始SpringMVC4.1.为什么要学SpringMVC?4.2.SpringMVC的中重点DispatcherServlet4.3.SpringMVC项目的搭建4.4.MVC框架要做哪些事情?4.5.可能会遇到的问题 5.SpringMVC的执行原理6.使用注解开发SpringMVC7.Controller控制总结8.RestF…...
一条 SQL 更新语句如何执行的
Server 层 存储引擎层 总流程 查询语句 连接器 查询缓存 分析器 优化器 执行器 更新语句 redo log(节省的是随机写磁盘的 IO 消耗(转成顺序写&#x…...
Github上哪些好用的安全工具1
专注于web漏洞挖掘、内网渗透、免杀和代码审计,感谢各位师傅的关注!网安之路漫长,与君共勉! URLFinder 一款快速提取网页信息的工具。该项目可以快速爬取网页上的 URL 地址、JS 文件里的 API 接口等信息,支持批量抓取…...
手写Mybatis自动填充插件
目录 一、Mybatis插件简介🥙二、工程创建及前期准备工作🥫实现代码配置文件 三、插件核心代码实现🍗四、测试🥓 一、Mybatis插件简介🥙 Mybatis插件运行原理及自定义插件_简述mybatis的插件运行原理,以及如何编写一个…...
upload文件上传漏洞复现
什么是文件上传漏洞: 文件上传漏洞是指由于程序员在对用户文件上传部分的控制不足或者处理缺陷,而导致的用户可以越过其本身权限向服务器上上传可执行的动态脚本文件。这里上传的文件可以是木马,病毒,恶意脚本或者WebShell等。“…...
Docker 安装部署 SqlServer 数据库
Docker 安装部署 SqlServer 数据库 背景: 最近在开发数据中台数据集成模块,需要对接大量的数据做测试, 由于SqlServer 下载安装会耗费大量时间,所以采用 Docker 安装 Sqlserver 的方式部署数据库。 1、拉去 sqlserver 镜像 …...
cmath 中cos sin等常用函数的坑(弧度角度换算)
cmath中三角函数的输入是弧度,不是角度.忘了这件事,找bug找了好久! 弧度是旧称弪。在数学和物理中,弧度是角的度量单位。它是由国际单位制导出的单位,单位缩写是rad。弧度是指在一个圆中,弧长和半径之比,即|弧度|弧长半径。 角度…...
深度解析HTTP反向代理-okey proxy
反向代理這個概念可能並不常見,但其實它對於提升網路安全和訪問速度方面發揮著很大作用。 HTTP反向代理(HTTP Reverse Proxy)是一種特殊的代理伺服器,首先它能夠接收互聯網上的連接請求,然後將這些請求轉發給內部網路…...
SwinIR训练报错解决
swinir训练报错解决 记录swinir图像超分重建算法复现过程中的报错信息,并提供相应的解决方案 报错信息 UserWarning: torch.meshgrid: in an upcoming release, it will be required to pass the indexing argument. (Triggered internally at C:\actions-runner\_work\pyto…...
C++类和对象一
#include <iostream> using namespace std;//设计一个学生类 class CStudent {public: //公有成员void InputData(){cout << "请输入学号";cin >> sno;cout << "请输入姓名";cin >> sname;cout << "请输入分…...
Linux之线程互斥
目录 一、问题引入 二、线程互斥 1、相关概念 2、加锁保护 1、静态分配 2、动态分配 3、锁的原理 4、死锁 三、可重入与线程安全 1、概念 2、常见的线程不安全的情况 3、常见的线程安全的情况 4、常见不可重入的情况 5、常见可重入的情况 6、可重入与线程安全联系…...
8051单片机PDATA与XDATA存储访问优化解析
1. PDATA与XDATA变量生成的指令解析在8051单片机开发中,外部数据存储器的访问方式直接影响程序效率和硬件设计。作为从业十余年的嵌入式工程师,我经常需要针对不同存储区域优化代码。PDATA和XDATA作为两种常见的外部数据存储模式,其指令生成机…...
Spark Transformer:稀疏化技术提升大模型计算效率
1. Spark Transformer架构解析在深度学习领域,Transformer模型已经成为自然语言处理和多模态任务的事实标准架构。然而,随着模型规模的不断扩大和序列长度的持续增长,计算效率问题日益突出。2025年提出的Spark Transformer通过创新性地重新激…...
【教育部“人工智能+教育”试点标杆】:从零部署到常态化应用——某省327所乡村校6个月落地实录
更多请点击: https://intelliparadigm.com 第一章:PlayAI教育领域应用案例 PlayAI 作为面向教育场景的轻量级AI交互平台,已在多个K12及职业教育机构落地实践,聚焦于个性化学习路径生成、实时学情反馈与智能助教协同三大方向。其核…...
独立开发者如何利用 Taotoken 的 Token Plan 套餐以更优成本启动 AI 项目
🚀 告别海外账号与网络限制!稳定直连全球优质大模型,限时半价接入中。 👉 点击领取海量免费额度 独立开发者如何利用 Taotoken 的 Token Plan 套餐以更优成本启动 AI 项目 对于独立开发者或小型工作室而言,在项目启动…...
工业级大模型学习之路021:LangChain零基础入门教程(第四篇):文档加载与文本分块技术
一、文档处理是 RAG 系统的基石1.1 为什么文档处理决定了 RAG 系统的上限?RAG 系统的核心逻辑是 **"检索相关文档片段 → 喂给大模型生成回答"**,整个流程的质量完全依赖于文档处理环节:如果文档解析失败,再好的检索和生…...
Unity C#方法设计实战:从参数传递到跨脚本调用
1. 这不是语法课,是写代码时每天要面对的“沟通现场”刚带完一批Unity新手做小项目,有个现象特别明显:很多人能背出“方法就是函数”“参数分值传递和引用传递”,但一到实际写代码就卡壳——比如想让角色跳跃时播放音效࿰…...
【限时公开】我们压测了23个开源AI Agent框架,仅2个支持亚秒级SQL生成+自动schema纠错(测试报告PDF已备)
更多请点击: https://codechina.net 第一章:AI Agent数据分析应用 AI Agent 正在重塑数据分析的范式——它不再依赖人工编写 SQL 或手动配置 ETL 流程,而是通过自然语言理解任务意图、自主调用工具、迭代验证结果,并生成可解释的…...
5分钟掌握NoFences:告别杂乱桌面的免费桌面整理终极指南
5分钟掌握NoFences:告别杂乱桌面的免费桌面整理终极指南 【免费下载链接】NoFences 🚧 Open Source Stardock Fences alternative 项目地址: https://gitcode.com/gh_mirrors/no/NoFences 你是否每天都要面对一个布满杂乱图标的Windows桌面&#…...
联发科MT6833与MT6853 5G核心板:规格对比与产品选型实战指南
1. 项目概述:两款5G安卓核心板的定位与价值在当前的移动设备开发领域,尤其是面向中高端市场的智能手机、平板电脑以及各类智能终端,选择一颗性能强劲、功能集成度高且成本可控的核心处理器平台,是决定产品成败的关键。联发科&…...
各种“地”—— 各种“GND”
GND,指的是电线接地端的简写。代表地线或0线。电路图上和电路板上的GND(Ground)代表地线或0线.GND就是公共端的意思,也可以说是地,但这个地并不是真正意义上的地。是出于应用而假设的一个地,对于电源来说,它就是一个电…...
