基于Spring boot轻松实现一个多数据源框架

2023-10-18 21:45

本文主要是介绍基于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轻松实现一个多数据源框架的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!


原文地址:
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.chinasem.cn/article/235320

相关文章

SpringBoot实现多环境配置文件切换

《SpringBoot实现多环境配置文件切换》这篇文章主要为大家详细介绍了如何使用SpringBoot实现多环境配置文件切换功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1. 示例代码结构2. pom文件3. application文件4. application-dev文

Python FastAPI实现JWT校验的完整指南

《PythonFastAPI实现JWT校验的完整指南》在现代Web开发中,构建安全的API接口是开发者必须面对的核心挑战之一,本文将深入探讨如何基于FastAPI实现JWT(JSONWebToken... 目录一、JWT认证的核心原理二、项目初始化与环境配置三、安全密码处理机制四、JWT令牌的生成与验证五、

JavaScript实战:智能密码生成器开发指南

本文通过JavaScript实战开发智能密码生成器,详解如何运用crypto.getRandomValues实现加密级随机密码生成,包含多字符组合、安全强度可视化、易混淆字符排除等企业级功能。学习密码强度检测算法与信息熵计算原理,获取可直接嵌入项目的完整代码,提升Web应用的安全开发能力 目录

Python使用Turtle实现精确计时工具

《Python使用Turtle实现精确计时工具》这篇文章主要为大家详细介绍了Python如何使用Turtle实现精确计时工具,文中的示例代码讲解详细,具有一定的借鉴价值,有需要的小伙伴可以参考一下... 目录功能特点使用方法程序架构设计代码详解窗口和画笔创建时间和状态显示更新计时器控制逻辑计时器重置功能事件

Linux给磁盘扩容(LVM方式)的方法实现

《Linux给磁盘扩容(LVM方式)的方法实现》本文主要介绍了Linux给磁盘扩容(LVM方式)的方法实现,涵盖PV/VG/LV概念及操作步骤,具有一定的参考价值,感兴趣的可以了解一下... 目录1 概念2 实战2.1 相关基础命令2.2 开始给LVM扩容2.3 总结最近测试性能,在本地打数据时,发现磁盘空

Golang实现Redis分布式锁(Lua脚本+可重入+自动续期)

《Golang实现Redis分布式锁(Lua脚本+可重入+自动续期)》本文主要介绍了Golang分布式锁实现,采用Redis+Lua脚本确保原子性,持可重入和自动续期,用于防止超卖及重复下单,具有一定... 目录1 概念应用场景分布式锁必备特性2 思路分析宕机与过期防止误删keyLua保证原子性可重入锁自动

golang 对象池sync.Pool的实现

《golang对象池sync.Pool的实现》:本文主要介绍golang对象池sync.Pool的实现,用于缓存和复用临时对象,以减少内存分配和垃圾回收的压力,下面就来介绍一下,感兴趣的可以了解... 目录sync.Pool的用法原理sync.Pool 的使用示例sync.Pool 的使用场景注意sync.

C++ HTTP框架推荐(特点及优势)

《C++HTTP框架推荐(特点及优势)》:本文主要介绍C++HTTP框架推荐的相关资料,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录1. Crow2. Drogon3. Pistache4. cpp-httplib5. Beast (Boos

IDEA实现回退提交的git代码(四种常见场景)

《IDEA实现回退提交的git代码(四种常见场景)》:本文主要介绍IDEA实现回退提交的git代码(四种常见场景),具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1.已提交commit,还未push到远端(Undo Commit)2.已提交commit并push到

Kotlin Compose Button 实现长按监听并实现动画效果(完整代码)

《KotlinComposeButton实现长按监听并实现动画效果(完整代码)》想要实现长按按钮开始录音,松开发送的功能,因此为了实现这些功能就需要自己写一个Button来解决问题,下面小编给大... 目录Button 实现原理1. Surface 的作用(关键)2. InteractionSource3.