基于Spring boot轻松实现一个多数据源框架
Spring Boot 提供了 Data JPA 的包,允许你使用类似 ORM 的接口连接到 RDMS。它很容易使用和实现,只需要在 pom.xml 中添加一个条目(如果使用的是 Maven,Gradle 则是在 build.gradle 文件中)。
<dependencies><!-- Spring boot 依赖 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency> <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-jpa</artifactId></dependency>
</dependencies>
在Main Spring Application类中添加 2 个注释:
@SpringBootApplication
@EnableJpaRepositories
@EnableAutoConfiguration
public class SpringMainApplication {public static void main(String[] args) {SpringApplication.run(SpringMainApplication.class, args);}
}
最后添加一个数据库连接包,配置数据库连接即可实现与数据库通信。
接下来,我们开始配置多数据源连接。
注意:多个数据库应该具有相同的驱动程序。无法连接到不同的数据库,如 MySql 和 Postgres SQL 数据库。数据库必须相同。此外,数据库模式必须相同,不同模式的 2 个数据库无法进行连接。
多数据源有哪些应用场景?
1.支持具有相同模式的同一应用程序内的多租户。
2.动态模拟多个环境数据库上的行为 ,而不需要重新启动应用程序。 例如,你可以动态连接到开发数据库或 QA 数据库,而无需重新启动应用程序。
3.支持多个数据库来模拟各种自动化测试场景。不同数据库可能具有不同的配置和静态信息,意味着你可以用一个自动化测试脚本覆盖多个测试用例。
4.在同一个应用程序中支持多个组织。根据用户登录,可以动态决定他们的数据应进入哪个组织的数据库。
5.一次性为多个数据库插入数据。例如,你有一个从脚本创建数据的批处理作业,你可以一次性连接到多个数据库,并对所有这些数据库运行脚本,而无需指向不同的应用程序或重新启动服务器来执行此操作。
多数据源示意图如下:

第一步:添加 pom 依赖
<dependencies><!-- Spring boot dependencies --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-actuator</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-jpa</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!-- Swagger dependencies --><dependency><groupId>io.springfox</groupId><artifactId>springfox-swagger2</artifactId><version>2.9.2</version></dependency><dependency><groupId>io.springfox</groupId><artifactId>springfox-swagger-ui</artifactId><version>2.9.2</version></dependency><!-- lombok dependency --><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.12</version><scope>provided</scope></dependency><!-- Database dependency --><dependency><groupId>org.postgresql</groupId><artifactId>postgresql</artifactId></dependency><!-- test dependencies --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope><exclusions><exclusion><groupId>org.junit.vintage</groupId><artifactId>junit-vintage-engine</artifactId></exclusion></exclusions></dependency></dependencies><dependencyManagement><dependencies><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-parent</artifactId><version>${spring-cloud-dependencies.version}</version><type>pom</type><scope>import</scope></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-gcp-dependencies</artifactId><version>${project.version}</version><type>pom</type><scope>import</scope></dependency></dependencies></dependencyManagement><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-surefire-plugin</artifactId></plugin></plugins></build>
第二步:添加数据库连接配置
app.datasource.db1.jdbc-url=jdbc:postgresql://db1.com:5432/dbname1
app.datasource.db1.username=postgres
app.datasource.db1.password=passwordapp.datasource.db2.jdbc-url=jdbc:postgresql://db2.com:5432/dbname2
app.datasource.db2.username=postgres
app.datasource.db2.password=passwordapp.datasource.db3.jdbc-url=jdbc:postgresql://db3.com:5432/dbname3
app.datasource.db3.username=postgres
app.datasource.db3.password=password
这是 3 个独立的 PostgresSQL 实例,具有相同的模式但具有不同的数据。
第三步:添加多数据库配置。
首先,在 Spring 应用程序主文件中添加注解:
@SpringBootApplication
@EnableJpaRepositories
@EnableAutoConfiguration
public class MultidatabaseApplication {public static void main(String[] args) {SpringApplication.run(MultidatabaseApplication.class, args);}
}
添加配置类:
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(entityManagerFactoryRef = "multiEntityManager",transactionManagerRef = "multiTransactionManager")
@EntityScan("com.sample.client.repositories.dto.entity")
public class DatabaseConfiguration {//添加 JPA 实体路径private final String PACKAGE_SCAN = "com.sample.client.repositories.dto.entity";// 将db1设置为主数据库@Primary@Bean(name = "db1DataSource")@ConfigurationProperties("app.datasource.db1")public DataSource db1DataSource() {return DataSourceBuilder.create().type(HikariDataSource.class).build();}//db2连接数据源注入@Bean(name = "db2DataSource")@ConfigurationProperties("app.datasource.db2")public DataSource db2DataSource() {return DataSourceBuilder.create().type(HikariDataSource.class).build();}//db3连接数据源注入@Bean(name = "db3DataSource")@ConfigurationProperties("app.datasource.db3")public DataSource db3DataSource() {return DataSourceBuilder.create().type(HikariDataSource.class).build();}//多数据源配置@Bean(name = "multiRoutingDataSource")public DataSource multiRoutingDataSource() {Map<Object, Object> targetDataSources = new HashMap<>();targetDataSources.put(ClientNames.DB1, db1DataSource());targetDataSources.put(ClientNames.DB2, db2DataSource());targetDataSources.put(ClientNames.DB3, db3DataSource());MultiRoutingDataSource multiRoutingDataSource = new MultiRoutingDataSource();multiRoutingDataSource.setDefaultTargetDataSource(db1DataSource());multiRoutingDataSource.setTargetDataSources(targetDataSources);return multiRoutingDataSource;}//多实体配置代码@Bean(name = "multiEntityManager")public LocalContainerEntityManagerFactoryBean multiEntityManager() {LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();em.setDataSource(multiRoutingDataSource());em.setPackagesToScan(PACKAGE_SCAN);HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();em.setJpaVendorAdapter(vendorAdapter);em.setJpaProperties(hibernateProperties());return em;}@Bean(name = "multiTransactionManager")public PlatformTransactionManager multiTransactionManager() {JpaTransactionManager transactionManager= new JpaTransactionManager();transactionManager.setEntityManagerFactory(multiEntityManager().getObject());return transactionManager;}@Primary@Bean(name="entityManagerFactory")public LocalSessionFactoryBean dbSessionFactory() {LocalSessionFactoryBean sessionFactoryBean = new LocalSessionFactoryBean();sessionFactoryBean.setDataSource(multiRoutingDataSource());sessionFactoryBean.setPackagesToScan(PACKAGE_SCAN);sessionFactoryBean.setHibernateProperties(hibernateProperties());return sessionFactoryBean;}//添加 hibernate 属性private Properties hibernateProperties() {Properties properties = new Properties();properties.put("hibernate.show_sql", true);properties.put("hibernate.format_sql", true);properties.put("hibernate.dialect", "org.hibernate.dialect.PostgreSQLDialect");properties.put("hibernate.id.new_generator_mappings", false);properties.put("hibernate.jdbc.lob.non_contextual_creation", true);return properties;}
}
这样就完成了我们的多数据库配置。
com.sample.client.repositories.dto.entity — 此目录包含 3 个数据库通用的 JPA 实体。
MultiRoutingDataSource类是我们的实际实现,允许我们连接到多个数据库
接下来,我们还需要一个DBContextHolder类来保存数据库引用并在运行时动态更改数据库。
public class DBContextHolder {private static final ThreadLocal<ClientNames> contextHolder = new ThreadLocal<>();public static void setCurrentDb(ClientNames dbType) {contextHolder.set(dbType);}public static ClientNames getCurrentDb() {return contextHolder.get();}public static void clear() {contextHolder.remove();}
}
ClientNames枚举类如下:
public enum ClientNames {DB1, DB2, DB3
}
接下来我们需要对MultiRoutingDataSource进行重写:
public class MultiRoutingDataSource extends AbstractRoutingDataSource {@Overrideprotected Object determineCurrentLookupKey() {return DBContextHolder.getCurrentDb();}
}
determineCurrentLookupKey 方法用于决定应用程序应该动态连接到哪个数据库。
好了,我们的配置就完成了。接下来,我们测试下多数据源是否生效:
@RestController
@RequestMapping("/client")
public class ClientDataController {@Autowiredprivate ClientMasterService clientMasterService;@GetMapping("/{clientdb}")public String findFromDatabase(@PathVariable String clientdbName) {return clientMasterService.getClientNames(clientdbName);}
}
ClientMasterService实现如下:
@Service
public class ClientMasterService {@Autowiredprivate ClientMasterRepository clientMasterRepository;public String getClientNames(String client) {switch (client) {case "db1":DBContextHolder.setCurrentDb(ClientNames.DB1);break;case "db2":DBContextHolder.setCurrentDb(ClientNames.DB2);break;case "db3":DBContextHolder.setCurrentDb(ClientNames.DB3);break;}Entity1 e1 = clientMasterRepository.findByEntity1Name("John Doe");if(e1 != null) {return "found in database: " + client + " with id " + e1.getId();}return "found in " + client + " nada!";}
}
ClientMasterService使用DBContextHolder类根据从 Rest 端点传入的数据库名称(db1、db2 或 db3)设置要指向的数据库。
最后,编写 JPA Repository 基础代码:
@Repository
public interface ClientMasterRepository extends JpaRepository<Entity1, String> {Entity1 findByEntity1Name(String name);
}
Entity1 类如下:
@Entity
@Table(name = "entity1")
@Getter
@Setter
public class Entity1 implements Serializable {@Id@Column(name = "id", nullable = false)private Integer id;@Column(name = "entity1Name")private String entity1Name;
}
这样就完成了整个多数据源的配置!!!
总结
如果你有多租户需求,或者多环境测试需求等,可以自己尝试编写一个多数据源框架,也可以引入第三方库来解决此需求。
相关文章:
基于Spring boot轻松实现一个多数据源框架
Spring Boot 提供了 Data JPA 的包,允许你使用类似 ORM 的接口连接到 RDMS。它很容易使用和实现,只需要在 pom.xml 中添加一个条目(如果使用的是 Maven,Gradle 则是在 build.gradle 文件中)。 <dependencies>&l…...
vue前端实现打印功能并约束纸张大小---调用浏览器打印功能打印页面部分元素并固定纸张大小
需求是打印指定div实现小票打印功能。调用浏览器的自带打印功能只能实现打印可视区域,所以这里采用截图新窗口打开打印去实现此需求。 1.安装html2canvas库实现截图功能 npm install html2canvas --save2.在需要进行截图和打印的组件中,引入html2canvas…...
音乐播放器蜂鸣器ROM存储歌曲verilog,代码/视频
名称:音乐播放器蜂鸣器ROM存储歌曲 软件:Quartus 语言:Verilog 代码功能: 设计音乐播放器,要求至少包含2首歌曲,使用按键切换歌曲,使用开发板的蜂鸣器播放音乐,使用Quartus内的RO…...
Arduino Nano 引脚复用分析
近期开发的项目为气体传感器采集仪,综合需求,选取NANO作为主控,附属设备有 oled、旋转编码器、H桥板、蠕动泵、开关、航插等,主要是用现有接口怎么合理配置实现功能。 不管stm32 还是 Arduino 都要看清引脚图 D2 D3 引脚是两个外…...
Go 函数多返回值错误处理与error 类型介绍
Go 函数多返回值错误处理与error 类型介绍 文章目录 Go 函数多返回值错误处理与error 类型介绍一、error 类型与错误值构造1.1 Error 接口介绍1.2 构造错误值的方法1.2.1 使用errors包1.2.2 自定义错误类型 二、error 类型的好处2.1 第一点:统一了错误类型2.2 第二点…...
数论分块
本质就是利用取整分数值的块状分布。 UVA11526 H(n) 题意: 求 ∑ i 1 n n i \sum_{i1}^{n} \frac {n}{i} ∑i1nin。 解析: ⌊ n i ⌋ \lfloor \frac{n}{i} \rfloor ⌊in⌋ 只有 O ( n ) O(\sqrt n) O(n ) 种取值,考虑将相同值同…...
宏任务与微任务,代码执行顺序
js引擎工作进程是同步的。事件循环机制,事件队列。 脚本代码执行顺序,是先执行同步代码,遇到微任务,就把它推进任务队列中。每个宏任务完成后,再执行下一个宏任务。 宏任务有哪些: i/o读写 定时器setTi…...
正方形(Squares, ACM/ICPC World Finals 1990, UVa201)rust解法
有n行n列(2≤n≤9)的小黑点,还有m条线段连接其中的一些黑点。统计这些线段连成了多少个正方形(每种边长分别统计)。 行从上到下编号为1~n,列从左到右编号为1~n。边用H i j和V i j表示…...
【算法设计与分析qwl】伪码——顺序检索,插入排序
伪代码: 例子: 改进的顺序检索 Search(L,x)输入:数组L[1...n],元素从小到大排序,数x输出:若x在L中,输出x位置下标 j ,否则输出0 j <- 1 while j<n and x>L[j] do j <- j1 if x<…...
Uniapp路由拦截-自定义路由白名单
步骤一:新建routerIntercept.js文件 步骤二:routerIntercept文件中写入:(根据自己需要修改whiteList白名单中的页面路径和自己的逻辑处理) import Vue from vue // 白名单 const whiteList = [/pages/public/login,/pages/public/privacyAgreement, ]export default asy…...
在中国可以使用 HubSpot 吗?
当谈到市场营销和客户关系管理工具时,HubSpot通常是一家企业的首选。然而,对于许多中国的企业来说,一个重要的问题是:在中国可以使用HubSpot吗?这个问题涉及到不同的方面,包括政策法规、社交媒体平台、语言…...
Java的基础应用
Java是一种广泛应用于软件开发的编程语言,基础应用涵盖了很多方面。以下是Java的一些基础应用方面的介绍: 1. 控制流语句:Java中的程序流程控制语句分为选择语句和循环语句。选择语句包括if-else语句和switch语句,循环语句包括fo…...
【excel】列转行
列转行 工作中有一些数据是列表,现在需要转行 选表格内容:在excel表格中选中表格数据区域。点击复制:在选中表格区域处右击点击复制。点击选择性粘贴:在表格中鼠标右击点击选择性粘贴。勾选转置:在选择性粘勾选转置选…...
用Bing绘制「V我50」漫画;GPT-5业内交流笔记;LLM大佬的跳槽建议;Stable Diffusion生态全盘点第一课 | ShowMeAI日报
👀日报&周刊合集 | 🎡生产力工具与行业应用大全 | 🧡 点赞关注评论拜托啦! 🔥 美国升级AI芯片出口禁令,13家中国GPU企业被列入实体清单 nytimes.com/2023/10/05/technology/chip-makers-china-lobbying…...
Java身份证实名认证-阿里云API 【姓名、身份证号】
1. 阿里云API市场 https://market.aliyun.com/products/57126001/cmapi00053442.html?spm5176.2020520132.101.3.a6217218nxxEiy#skuyuncode47442000022 购买对应套餐 2. 复制AppCode https://market.console.aliyun.com/imageconsole/index.htm#/?_kl85e10 云市场-已购买服…...
ND协议——无状态地址自动配置 (SLAAC)
参考学习:计算机网络 | 思科网络 | 无状态地址自动配置 (SLAAC) | 什么是SLAAC_瘦弱的皮卡丘的博客-CSDN博客 与 IPv4 类似,可以手动或动态配置 IPv6 全局单播地址。但是,动态分配 IPv6 全局单播地址有两种方法: 如图所示&#…...
iOS开发UITableView的使用,区别Plain模式和Grouped模式
简单赘述一下 的创建步骤 // 创建UITableView self.tableView [[UITableView alloc] initWithFrame:self.view.bounds style:UITableViewStylePlain]; // 设置数据源和代理 self.tableView.dataSource self; self.tableView.delegate self; // 注册自定义UITableViewCe…...
css美化滚动条
/*定义滚动条高宽及背景 高宽分别对应横竖滚动条的尺寸*/ ::-webkit-scrollbar { width: 8px; height: 8px; background-color: rgba(0,0,0,.2); } /*定义滚动条轨道 内阴影圆角*/ ::-webkit-scrollbar-track { -webkit-box…...
【CANoe】XML Test Module使用实例
文章目录 一、实操步骤1、增加XML Test Module节点2、配置XML Test Module节点3、XML Test Module节点增加CAPL脚本(.can文件)4、文件夹结构5、使用仿真节点开始测试6、测试结果与测试报告7、同理,在Test Setup也可如此操作 一、实操步骤 1、增加XML Test Module节…...
oracle的update语句where条件后的索引字段为空时不执行
问题描述: update 表名 set age ‘23’ where id1 and name‘lili’; 当在oracle执行以上sql时执行成功,但是当传入的name为null时,sql不成立。我的表中id和name是联合唯一索引,以为name不会为空,但实际上name可以为空…...
[特殊字符] 智能合约中的数据是如何在区块链中保持一致的?
🧠 智能合约中的数据是如何在区块链中保持一致的? 为什么所有区块链节点都能得出相同结果?合约调用这么复杂,状态真能保持一致吗?本篇带你从底层视角理解“状态一致性”的真相。 一、智能合约的数据存储在哪里…...
大数据学习栈记——Neo4j的安装与使用
本文介绍图数据库Neofj的安装与使用,操作系统:Ubuntu24.04,Neofj版本:2025.04.0。 Apt安装 Neofj可以进行官网安装:Neo4j Deployment Center - Graph Database & Analytics 我这里安装是添加软件源的方法 最新版…...
OpenLayers 可视化之热力图
注:当前使用的是 ol 5.3.0 版本,天地图使用的key请到天地图官网申请,并替换为自己的key 热力图(Heatmap)又叫热点图,是一种通过特殊高亮显示事物密度分布、变化趋势的数据可视化技术。采用颜色的深浅来显示…...
ffmpeg(四):滤镜命令
FFmpeg 的滤镜命令是用于音视频处理中的强大工具,可以完成剪裁、缩放、加水印、调色、合成、旋转、模糊、叠加字幕等复杂的操作。其核心语法格式一般如下: ffmpeg -i input.mp4 -vf "滤镜参数" output.mp4或者带音频滤镜: ffmpeg…...
微服务商城-商品微服务
数据表 CREATE TABLE product (id bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 商品id,cateid smallint(6) UNSIGNED NOT NULL DEFAULT 0 COMMENT 类别Id,name varchar(100) NOT NULL DEFAULT COMMENT 商品名称,subtitle varchar(200) NOT NULL DEFAULT COMMENT 商…...
WordPress插件:AI多语言写作与智能配图、免费AI模型、SEO文章生成
厌倦手动写WordPress文章?AI自动生成,效率提升10倍! 支持多语言、自动配图、定时发布,让内容创作更轻松! AI内容生成 → 不想每天写文章?AI一键生成高质量内容!多语言支持 → 跨境电商必备&am…...
多模态图像修复系统:基于深度学习的图片修复实现
多模态图像修复系统:基于深度学习的图片修复实现 1. 系统概述 本系统使用多模态大模型(Stable Diffusion Inpainting)实现图像修复功能,结合文本描述和图片输入,对指定区域进行内容修复。系统包含完整的数据处理、模型训练、推理部署流程。 import torch import numpy …...
[大语言模型]在个人电脑上部署ollama 并进行管理,最后配置AI程序开发助手.
ollama官网: 下载 https://ollama.com/ 安装 查看可以使用的模型 https://ollama.com/search 例如 https://ollama.com/library/deepseek-r1/tags # deepseek-r1:7bollama pull deepseek-r1:7b改token数量为409622 16384 ollama命令说明 ollama serve #:…...
LangFlow技术架构分析
🔧 LangFlow 的可视化技术栈 前端节点编辑器 底层框架:基于 (一个现代化的 React 节点绘图库) 功能: 拖拽式构建 LangGraph 状态机 实时连线定义节点依赖关系 可视化调试循环和分支逻辑 与 LangGraph 的深…...
rknn toolkit2搭建和推理
安装Miniconda Miniconda - Anaconda Miniconda 选择一个 新的 版本 ,不用和RKNN的python版本保持一致 使用 ./xxx.sh进行安装 下面配置一下载源 # 清华大学源(最常用) conda config --add channels https://mirrors.tuna.tsinghua.edu.cn…...
