SpringBoot 项目配置动态数据源
目录
- 一、前言
- 二、操作
- 1、引入依赖
- 2、配置默认数据库 1
- 3、定义数据源实体和 Repository
- 4、定义动态数据源
- 5、配置数据源
- 6、定义切换数据源注解
- 7、定义切面类
- 8、使用注解切换数据源
一、前言
通过切面注解方式根据不同业务动态切换数据库
二、操作
1、引入依赖
<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-jpa</artifactId></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId></dependency><dependency><groupId>com.zaxxer</groupId><artifactId>HikariCP</artifactId></dependency>
</dependencies>
2、配置默认数据库 1
- 在 application.properties 或 application.yml 配置数据库 1 信息:
spring.datasource.url=jdbc:mysql://localhost:3306/db1
spring.datasource.username=root
spring.datasource.password=password
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
3、定义数据源实体和 Repository
- 数据源实体 DataSourceConfig.java:
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;@Entity
public class DataSourceEntity {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Long id;private String url;private String username;private String password;private String driverClassName;private String dataSourceKey; // 新增字段// Getters 和 Setterspublic Long getId() {return id;}public void setId(Long id) {this.id = id;}public String getUrl() {return url;}public void setUrl(String url) {this.url = url;}public String getUsername() {return username;}public void setUsername(String username) {this.username = username;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}public String getDriverClassName() {return driverClassName;}public void setDriverClassName(String driverClassName) {this.driverClassName = driverClassName;}public String getDataSourceKey() {return dataSourceKey;}public void setDataSourceKey(String dataSourceKey) {this.dataSourceKey = dataSourceKey;}
}
- Repository 接口 DataSourceConfigRepository.java:
import org.springframework.data.jpa.repository.JpaRepository;public interface DataSourceConfigRepository extends JpaRepository<DataSourceEntity, Long> {
}
4、定义动态数据源
- 动态数据源上下文持有者 DynamicDataSourceContextHolder.java:
public class DynamicDataSourceContextHolder {private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();public static void setDataSourceKey(String key) {contextHolder.set(key);}public static String getDataSourceKey() {return contextHolder.get();}public static void clearDataSourceKey() {contextHolder.remove();}
}
- 动态数据源类 DynamicDataSource.java:
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;public class DynamicDataSource extends AbstractRoutingDataSource {@Overrideprotected Object determineCurrentLookupKey() {return DynamicDataSourceContextHolder.getDataSourceKey();}
}
5、配置数据源
import com.zaxxer.hikari.HikariDataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;import javax.sql.DataSource;
import java.util.HashMap;
import java.util.List;
import java.util.Map;@Configuration
public class DataSourceConfig {@Autowiredprivate DataSourceConfigRepository dataSourceConfigRepository;@Primary@Beanpublic DataSource dataSource() {DynamicDataSource dynamicDataSource = new DynamicDataSource();Map<Object, Object> targetDataSources = new HashMap<>();// 从数据库 1 的 datasource 表加载所有数据源List<DataSourceEntity> dataSourceConfigs = dataSourceConfigRepository.findAll();for (DataSourceEntity config : dataSourceConfigs) {HikariDataSource dataSource = new HikariDataSource();dataSource.setJdbcUrl(config.getUrl());dataSource.setUsername(config.getUsername());dataSource.setPassword(config.getPassword());dataSource.setDriverClassName(config.getDriverClassName());targetDataSources.put(config.getId().toString(), dataSource);if ("db1".equals(config.getDataSourceKey())) { // 假设表中有一个字段表示数据源的 keydynamicDataSource.setDefaultTargetDataSource(dataSource);}}dynamicDataSource.setTargetDataSources(targetDataSources);return dynamicDataSource;}
}
6、定义切换数据源注解
import java.lang.annotation.*;@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataSourceSwitch {String value() default "db1";
}
7、定义切面类
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;import java.lang.reflect.Method;
import java.util.List;@Aspect
@Component
public class DataSourceSwitchAspect {@Autowiredprivate DataSourceConfigRepository dataSourceConfigRepository;@Before("@annotation(com.example.annotation.DataSourceSwitch)")public void before(JoinPoint point) {MethodSignature signature = (MethodSignature) point.getSignature();Method method = signature.getMethod();DataSourceSwitch dataSourceSwitch = method.getAnnotation(DataSourceSwitch.class);if (dataSourceSwitch != null) {String dataSourceKey = dataSourceSwitch.value();List<DataSourceEntity> dataSourceConfigs = dataSourceConfigRepository.findAll();for (DataSourceConfig config : dataSourceConfigs) {if (dataSourceKey.equals(config.getDataSourceKey())) {DynamicDataSourceContextHolder.setDataSourceKey(config.getId().toString());break;}}}}@After("@annotation(com.example.annotation.DataSourceSwitch)")public void after(JoinPoint point) {DynamicDataSourceContextHolder.clearDataSourceKey();}
}
8、使用注解切换数据源
import com.example.annotation.DataSourceSwitch;
import com.example.entity.User;
import com.example.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;import java.util.List;@Service
public class UserService {@Autowiredprivate UserRepository userRepository;@DataSourceSwitch("db1")public List<User> getUsersFromDb1() {return userRepository.findAll();}@DataSourceSwitch("db2") // 假设 db2 是从 datasource 表获取的数据源 keypublic List<User> getUsersFromDb2() {return userRepository.findAll();}
}
相关文章:
SpringBoot 项目配置动态数据源
目录 一、前言二、操作1、引入依赖2、配置默认数据库 13、定义数据源实体和 Repository4、定义动态数据源5、配置数据源6、定义切换数据源注解7、定义切面类8、使用注解切换数据源 一、前言 通过切面注解方式根据不同业务动态切换数据库 二、操作 1、引入依赖 <dependen…...
CSS基本选择器
1. 通配选择器 作用:可以选中所有的 HTML 元素。 语法: * { 属性名: 属性值; } 举例: <!DOCTYPE html> <html lang"zh-cn"> <head><meta charset"UTF-8"><meta name"viewport" …...
idea-代码补全快捷键
文章目录 前言idea-代码补全快捷键1. 基本补全2. 类型匹配补全3. 后缀补全4. 代码补全 前言 如果您觉得有用的话,记得给博主点个赞,评论,收藏一键三连啊,写作不易啊^ _ ^。 而且听说点赞的人每天的运气都不会太差,…...
基于SpringBoot+vue粮油商城小程序系统
粮油商城小程序为用户提供方便快捷的在线购物体验,包括大米、面粉、食用油、调味品等各种粮油产品的选购,用户可以浏览商品详情、对比价格、下单支付等操作。同时,商城还提供优惠活动、积分兑换等福利,让用户享受到更多实惠和便利…...
挪车小程序挪车二维码php+uniapp
一款基于FastAdminThinkPHP开发的匿名通知车主挪车微信小程序,采用匿名通话的方式,用户只能在有效期内拨打车主电话,过期失效,从而保护车主和用户隐私。提供微信小程序端和服务端源码,支持私有化部署。 更新日志 V1.0…...
企业内部知识库:安全协作打造企业智慧运营基石
内容概要 作为企业智慧运营的核心载体,企业内部知识库通过结构化的信息聚合与动态化的知识流动,为组织提供了从数据沉淀到价值转化的系统性框架。其底层架构以权限管理为核心,依托数据加密技术构建多层级访问控制机制,确保敏感信…...
网络安全推荐的视频教程 网络安全系列
第一章 网络安全概述 1.2.1 网络安全概念P4 网络安全是指网络系统的硬件、软件及其系统中的数据受到保护,不因偶然的或恶意的原因而遭到破坏、更改、泄露,系统连续可靠正常地运行,网络服务不中断。 1.2.3 网络安全的种类P5 (1…...
麒麟管家全新升级,运维问题“一键修复”
麒麟管家是openKylin社区SystemManager SIG开发的一款面向社区用户,能倾听用户烦恼和诉求,也能提供便利途径、解决用户问题的系统管理类应用,可以为用户提供问题反馈、系统垃圾清理、电脑故障排查、硬件设备管理及系统小工具等一站式服务&…...
MVCC(多版本并发控制)机制讲解
MVCC(Multi-Version Concurrency Control,多版本并发控制)这是一个在数据库管理系统中非常重要的技术,尤其是在处理并发事务时。别担心,我会用简单易懂的方式来讲解,让你轻松掌握它的原理和作用。 1. 什么是…...
React 与 Vue 对比指南 - 上
React 与 Vue 对比指南 - 上 本文将展示如何在 React 和 Vue 中实现常见功能,从基础渲染到高级状态管理 Hello 分别使用 react 和 vue 写一个 Hello World! react export default () > {return <div>Hello World!</div>; }vue <…...
开源协议深度解析:理解MIT、GPL、Apache等常见许可证
目录 前言1. MIT协议:自由而宽松的开源许可1.1 MIT协议的主要特点1.2 MIT协议的适用场景 2. GPL协议:自由软件的捍卫者2.1 GPL协议的核心理念2.2 GPL协议的适用场景 3. Apache License 2.0:开源与专利保护的平衡3.1 Apache License 2.0的主要…...
通用评估系统(五)- 前端部分总体说明
通用评估系统(五)- 前端部分总体说明 相关链接 Gitee地址通用评估系统(一)- 介绍通用评估系统(二)- 原型设计通用评估系统(三)- 前端部分通用评估系统(四)-…...
STM32GPIO
目录 GPIO基本结构GPIO位结构输入部分输出部分 输出模式GPIO模式浮空/上拉/下拉模拟输入开漏和推挽复用开漏/复用推挽 GPIO基本结构 GPIO是挂载在APB2总线上的外设。GPIO结构中的寄存器分为输入寄存器和输出寄存器,APB2总线通过输出寄存器向引脚发送数据,…...
MyBatis拦截器终极指南:从原理到企业级实战
在本篇文章中,我们将深入了解如何编写一个 MyBatis 拦截器,并通过一个示例来展示如何在执行数据库操作(如插入或更新)时,自动填充某些字段(例如 createdBy 和 updatedBy)信息。本文将详细讲解拦…...
SpringBoot启动失败之application.yml缩进没写好
修改前: spring前面空格了 报错输出:Failed to configure a DataSource: ‘url’ attribute is not specified and no embedded datasource could be configured. Reason: Failed to determine a suitable driver class Action: Consider the follow…...
【JavaScript】《JavaScript高级程序设计 (第4版) 》笔记-Chapter17-事件
十七、事件 事件 JavaScript 与 HTML 的交互是通过事件实现的,事件代表文档或浏览器窗口中某个有意义的时刻。可以使用仅在事件发生时执行的监听器(也叫处理程序)订阅事件。在传统软件工程领域,这个模型叫“观察者模式”ÿ…...
鸿蒙开发:V2版本装饰器之@Monitor装饰器
前言 本文代码案例基于Api13。 随着官方的迭代,在新的Api中,对于新的应用开发,官方已经建议直接使用V2所属的装饰器进行开发了,所以,能上手V2的尽量上手V2吧,毕竟,V2是V1的增强版本,…...
51单片机-外部中断
以外部中断0为例: 主程序中需要有以下代码: EA1; //打开总中断开关 EX01; //开外部中断0 IT00/1; 设置外部中断的触发方式 P3.2\P3.3为外部中断接口,通过控制P3.2口按键按下实现LED灯反转点亮 #include "reg52.h"typed…...
UE C++ UObject 功能的初步总结(结合官方文档)
一. Uboject的官方文档的个人理解 Objects in Unreal Engine | 虚幻引擎 5.5 文档 | Epic Developer Community 目录在此 1.垃圾回收:上篇文章简单介绍过,UObject的创建和回收。本身是很复杂的功能,后续会接着单项深入分析。 2.引用更新 1. 反射:之前…...
DeepSeek和ChatGPT的全面对比
一、模型基础架构对比(2023技术版本) 维度DeepSeekChatGPT模型家族LLAMA架构改进GPT-4优化版本参数量级开放7B/35B/120B闭源175B位置编码RoPE NTK扩展ALiBiAttention机制FlashAttention-3FlashAttention-2激活函数SwiGLU ProGeGLU训练框架DeepSpeedMeg…...
DockerHub与私有镜像仓库在容器化中的应用与管理
哈喽,大家好,我是左手python! Docker Hub的应用与管理 Docker Hub的基本概念与使用方法 Docker Hub是Docker官方提供的一个公共镜像仓库,用户可以在其中找到各种操作系统、软件和应用的镜像。开发者可以通过Docker Hub轻松获取所…...
Java如何权衡是使用无序的数组还是有序的数组
在 Java 中,选择有序数组还是无序数组取决于具体场景的性能需求与操作特点。以下是关键权衡因素及决策指南: ⚖️ 核心权衡维度 维度有序数组无序数组查询性能二分查找 O(log n) ✅线性扫描 O(n) ❌插入/删除需移位维护顺序 O(n) ❌直接操作尾部 O(1) ✅内存开销与无序数组相…...
大语言模型如何处理长文本?常用文本分割技术详解
为什么需要文本分割? 引言:为什么需要文本分割?一、基础文本分割方法1. 按段落分割(Paragraph Splitting)2. 按句子分割(Sentence Splitting)二、高级文本分割策略3. 重叠分割(Sliding Window)4. 递归分割(Recursive Splitting)三、生产级工具推荐5. 使用LangChain的…...
学习STC51单片机31(芯片为STC89C52RCRC)OLED显示屏1
每日一言 生活的美好,总是藏在那些你咬牙坚持的日子里。 硬件:OLED 以后要用到OLED的时候找到这个文件 OLED的设备地址 SSD1306"SSD" 是品牌缩写,"1306" 是产品编号。 驱动 OLED 屏幕的 IIC 总线数据传输格式 示意图 …...
04-初识css
一、css样式引入 1.1.内部样式 <div style"width: 100px;"></div>1.2.外部样式 1.2.1.外部样式1 <style>.aa {width: 100px;} </style> <div class"aa"></div>1.2.2.外部样式2 <!-- rel内表面引入的是style样…...
【JavaWeb】Docker项目部署
引言 之前学习了Linux操作系统的常见命令,在Linux上安装软件,以及如何在Linux上部署一个单体项目,大多数同学都会有相同的感受,那就是麻烦。 核心体现在三点: 命令太多了,记不住 软件安装包名字复杂&…...
Android第十三次面试总结(四大 组件基础)
Activity生命周期和四大启动模式详解 一、Activity 生命周期 Activity 的生命周期由一系列回调方法组成,用于管理其创建、可见性、焦点和销毁过程。以下是核心方法及其调用时机: onCreate() 调用时机:Activity 首次创建时调用。…...
SiFli 52把Imagie图片,Font字体资源放在指定位置,编译成指定img.bin和font.bin的问题
分区配置 (ptab.json) img 属性介绍: img 属性指定分区存放的 image 名称,指定的 image 名称必须是当前工程生成的 binary 。 如果 binary 有多个文件,则以 proj_name:binary_name 格式指定文件名, proj_name 为工程 名&…...
Docker 本地安装 mysql 数据库
Docker: Accelerated Container Application Development 下载对应操作系统版本的 docker ;并安装。 基础操作不再赘述。 打开 macOS 终端,开始 docker 安装mysql之旅 第一步 docker search mysql 》〉docker search mysql NAME DE…...
【C++特殊工具与技术】优化内存分配(一):C++中的内存分配
目录 一、C 内存的基本概念 1.1 内存的物理与逻辑结构 1.2 C 程序的内存区域划分 二、栈内存分配 2.1 栈内存的特点 2.2 栈内存分配示例 三、堆内存分配 3.1 new和delete操作符 4.2 内存泄漏与悬空指针问题 4.3 new和delete的重载 四、智能指针…...
