基于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可以为空…...

【题解-洛谷】B4295 [蓝桥杯青少年组国赛 2022] 报数游戏
题目:B4295 [蓝桥杯青少年组国赛 2022] 报数游戏 题目描述 某班级男生人数为 X X X 人,女生人数为 Y Y Y 人,现全班同学围成一个圆圈,并按照顺时针方向为每名同学编号(从 1 1 1 到 X Y XY XY)。现给…...

什么是 WPF 技术?什么是 WPF 样式?下载、安装、配置、基本语法简介教程
什么是 WPF 技术?什么是 WPF 样式?下载、安装、配置、基本语法简介教程 摘要 WPF教程、WPF开发、.NET 8 WPF、Visual Studio 2022 WPF、WPF下载、WPF安装、WPF配置、WPF样式、WPF样式详解、XAML语法、XAML基础、MVVM架构、数据绑定、依赖属性、资源字典…...

Java String的使用续 -- StringBuilder类和StringBuffer
文章目录 字符串的不可变性StringBuilder和StringBuffer函数使用 字符串的不可变性 字符串不可变是因为有private修饰,只能在类的内部使用不可以在类外使用,因此使用时是不可以修改字符串的 public class test {public static void main(String[] args…...
WEB安全--RCE--webshell HIDS bypass4
继WEB安全--RCE--webshell HIDS bypass3的补充: 十三、时间开关 webshell: <?php ini_set("display_errors",1); function foo($test, $bar FSYSTEM) {echo $test . $bar; } $function new ReflectionFunction(foo); $q new ParseEr…...
在 Ubuntu 服务器上 下载 Clash 文件使用代理
文件Clash.Verge_1.3.8_x64_portable.zip 在 Ubuntu 服务器上不能使用这个Clash 文件**,我们需要的是 Clash.Meta 而不是 Clash Verge GUI 客户端 也就是 Clash Verge GUI 客户端的 Windows 版本,是给 Windows 桌面环境用的图形界面,不适用…...
微信小程序页面嵌套web-view点击系统导航返回时进行弹窗处理
实现效果:微信小程序页面嵌套web-view点击系统导航返回时进行弹窗处理 首先在web-view里是不可实现的(据我了解下来) 参考小程序文档:page-container 大致逻辑: 1、page-container可实现页面离开前拦截 2、由于web-vie…...

安装 Node.js 和配置 cnpm 镜像源
一、安装 Node.js 方式一:官网下载(适合所有系统) 访问 Node.js 官网 推荐选择 LTS(长期支持)版本,点击下载安装包。 根据系统提示一步步完成安装。 方式二:通过包管理器安装(建…...

5.28 后端面经
为什么golang在并发环境下更有优势 Go语言(Golang)在并发环境下的优势主要源自其设计哲学和内置的并发机制,这些机制在语言层面提供了高效、简洁且安全的并发编程工具。以下是其核心优势的详细分析: 1. Goroutine:轻量…...
青少年编程与数学 02-020 C#程序设计基础 11课题、可视化编程
青少年编程与数学 02-020 C#程序设计基础 11课题、可视化编程 一、可视化编程1. 降低学习门槛2. 提高学习兴趣3. 便于学习和掌握4. 为后续学习打下基础5. 适合不同年龄段和背景的初学者6. 适合初学者的可视化编程工具 二、可视化编程适合初学者1. 降低学习门槛2. 提高学习兴趣3…...
前后端传输 Long 类型数据时(时间戳,雪花算法ID),精度丢失的根本原因
前后端传输 Long 类型数据时,精度丢失的根本原因是 JavaScript 的 Number 类型无法精确表示超过 53 位(64 位双精度浮点数)的整数,而 Java 的 Long 类型是 64 位整数。当后端返回的 Long 值超过 2^53-1(即 90071992547…...