SpringBoot中Druid連接池與多數(shù)據(jù)源切換的方法
多數(shù)據(jù)源切換原理
多數(shù)據(jù)源切換的原理主要基于 Spring 的 AbstractRoutingDataSource 類(lèi)。AbstractRoutingDataSource 類(lèi)允許根據(jù)運(yùn)行時(shí)上下文動(dòng)態(tài)選擇數(shù)據(jù)源。其核心在于實(shí)現(xiàn) determineCurrentLookupKey 方法,該方法決定當(dāng)前操作使用哪個(gè)數(shù)據(jù)源。 AbstractRoutingDataSource 實(shí)現(xiàn)多數(shù)據(jù)源切換的原理:
1. 數(shù)據(jù)源映射
AbstractRoutingDataSource
內(nèi)部維護(hù)了一個(gè)映射(Map),用于存儲(chǔ)數(shù)據(jù)源標(biāo)識(shí)(key)和對(duì)應(yīng)的數(shù)據(jù)源實(shí)例(value)。這個(gè)映射允許根據(jù)數(shù)據(jù)源標(biāo)識(shí)快速查找和獲取對(duì)應(yīng)的數(shù)據(jù)源。
2. 數(shù)據(jù)源標(biāo)識(shí)的確定
AbstractRoutingDataSource
提供了一個(gè)抽象方法determineCurrentLookupKey()
,該方法用于確定當(dāng)前需要使用的數(shù)據(jù)源標(biāo)識(shí)。這個(gè)方法需要由子類(lèi)實(shí)現(xiàn),以返回當(dāng)前線程或請(qǐng)求應(yīng)該使用的數(shù)據(jù)源標(biāo)識(shí)。
3. 數(shù)據(jù)源的選擇與連接獲取
- 當(dāng)應(yīng)用程序需要獲取數(shù)據(jù)庫(kù)連接時(shí),
AbstractRoutingDataSource
的getConnection()
方法會(huì)被調(diào)用。這個(gè)方法首先調(diào)用determineCurrentLookupKey()
方法來(lái)獲取當(dāng)前的數(shù)據(jù)源標(biāo)識(shí),然后根據(jù)這個(gè)標(biāo)識(shí)從內(nèi)部映射中查找對(duì)應(yīng)的數(shù)據(jù)源。 - 一旦找到了對(duì)應(yīng)的數(shù)據(jù)源,
AbstractRoutingDataSource
就會(huì)調(diào)用該數(shù)據(jù)源的getConnection()
方法來(lái)獲取實(shí)際的數(shù)據(jù)庫(kù)連接,并將這個(gè)連接返回給應(yīng)用程序。
4. 數(shù)據(jù)源切換的實(shí)現(xiàn)
- 為了實(shí)現(xiàn)數(shù)據(jù)源的動(dòng)態(tài)切換,通常會(huì)在子類(lèi)中重寫(xiě)
determineCurrentLookupKey()
方法,并根據(jù)當(dāng)前的上下文(如線程變量)來(lái)確定返回的數(shù)據(jù)源標(biāo)識(shí)。 - 此外,通常會(huì)使用
ThreadLocal
來(lái)存儲(chǔ)每個(gè)線程的數(shù)據(jù)源標(biāo)識(shí),這樣每個(gè)線程都可以獨(dú)立地切換數(shù)據(jù)源而不會(huì)互相干擾。
實(shí)現(xiàn)步驟
1. 依賴(lài)
引入 MySQL 和 Druid 的依賴(lài)
<dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.2.6</version> </dependency>
2. 配置數(shù)據(jù)源
在 application.yml
文件中配置多個(gè)數(shù)據(jù)源。
spring: datasource: type: com.alibaba.druid.pool.DruidDataSource druid: datasource1: url: jdbc:mysql://localhost:3306/master_db username: root password: password initial-size: 5 min-idle: 5 max-active: 20 max-wait: 60000 datasource2: url: jdbc:mysql://localhost:3306/slave_db username: root password: password initial-size: 5 min-idle: 5 max-active: 20 max-wait: 60000
3. 創(chuàng)建數(shù)據(jù)源配置類(lèi)
創(chuàng)建一個(gè)配置類(lèi)來(lái)定義數(shù)據(jù)源 Bean。
@Configuration public class DataSourceConfig { @Bean @ConditionalOnProperty(prefix = "spring.datasource.druid", name = "datasource1") @ConfigurationProperties(prefix = "spring.datasource.druid.datasource1") public DataSource dataSource1() { return DruidDataSourceBuilder.create().build(); } @Bean @ConditionalOnProperty(prefix = "spring.datasource.druid", name = "datasource2") @ConfigurationProperties(prefix = "spring.datasource.druid.datasource2") public DataSource dataSource2() { return DruidDataSourceBuilder.create().build(); } @Bean @Primary public DataSource dynamicDataSource() { DynamicDataSource dynamicDataSource = new DynamicDataSource(); dynamicDataSource.setDefaultTargetDataSource(dataSource1()); Map<Object, Object> targetDataSources = new HashMap<>(); targetDataSources.put("dataSource1", dataSource1()); targetDataSources.put("dataSource2", dataSource2()); dynamicDataSource.setTargetDataSources(targetDataSources); return dynamicDataSource; } }
4. 實(shí)現(xiàn) AbstractRoutingDataSource
創(chuàng)建一個(gè)繼承自 AbstractRoutingDataSource
的類(lèi),并實(shí)現(xiàn) determineCurrentLookupKey
方法。
public class DynamicDataSource extends AbstractRoutingDataSource { @Override protected Object determineCurrentLookupKey() { return DataSourceContextHolder.getDataSource(); } }
5. 創(chuàng)建 DataSourceContextHolder
創(chuàng)建一個(gè)工具類(lèi)來(lái)保存當(dāng)前線程的數(shù)據(jù)源信息。
public class DataSourceContextHolder { private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>(); public static void setDataSource(String dataSource) { CONTEXT_HOLDER.set(dataSource); } public static String getDataSource() { return CONTEXT_HOLDER.get(); } public static void clearDataSource() { CONTEXT_HOLDER.remove(); } }
6. AOP動(dòng)態(tài)切換數(shù)據(jù)源
使用 AOP 在方法執(zhí)行前后切換數(shù)據(jù)源。
import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; import org.springframework.stereotype.Component; @Aspect @Component public class DataSourceAspect { // 方法或者類(lèi)上的橫切點(diǎn) @Pointcut("@annotation(dataSource) || @within(dataSource)") public void dataSourcePointcut(DataSource dataSource) {} @Before("dataSourcePointcut(dataSource)") public void switchDataSource(JoinPoint joinPoint, DataSource dataSource) { // 從注解中獲取數(shù)據(jù)源標(biāo)識(shí) String dataSourceKey = dataSource.value(); // 切換到指定的數(shù)據(jù)源 DataSourceContextHolder.setDataSourceType(dataSourceKey); } @AfterReturning(pointcut = "dataSourcePointcut(dataSource)", returning = "result") public void restoreDataSource(JoinPoint joinPoint, DataSource dataSource, Object result) { // 恢復(fù)默認(rèn)數(shù)據(jù)源(可選) DataSourceContextHolder.clearDataSourceType(); } } /** * 自定義注解 */ @Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface DataSource { String value() default "dataSource1"; }
7. 使用自定義注解
在需要切換數(shù)據(jù)源的方法上使用自定義注解。
注意
- 數(shù)據(jù)源切換的邏輯應(yīng)該盡可能簡(jiǎn)單和高效,以避免對(duì)應(yīng)用程序性能產(chǎn)生負(fù)面影響。
- 在切換數(shù)據(jù)源時(shí),需要注意事務(wù)管理的問(wèn)題,確保在同一個(gè)事務(wù)中只使用同一個(gè)數(shù)據(jù)源。
到此這篇關(guān)于SpringBoot中Druid連接池與多數(shù)據(jù)源切換的方法的文章就介紹到這了,更多相關(guān)SpringBoot Druid與數(shù)據(jù)源切換內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- SpringBoot整合mybatis使用Druid做連接池的方式
- Springboot中加入druid連接池
- springboot2.0配置連接池(hikari、druid)的方法
- SpringBoot整合Druid實(shí)現(xiàn)數(shù)據(jù)庫(kù)連接池和監(jiān)控
- springboot項(xiàng)目整合druid數(shù)據(jù)庫(kù)連接池的實(shí)現(xiàn)
- springboot集成druid連接池配置的方法
- springboot整合druid連接池的步驟
- SpringBoot使用 druid 連接池來(lái)優(yōu)化分頁(yè)語(yǔ)句
- SpringBoot整合Druid數(shù)據(jù)庫(kù)連接池的方法
- 解決Spring Boot中Druid連接池“discard long time none received connection“警告
相關(guān)文章
Spring?invokeBeanFactoryPostProcessors方法刨析源碼
invokeBeanFactoryPostProcessors該方法會(huì)實(shí)例化所有BeanDefinitionRegistryPostProcessor和BeanFactoryPostProcessor的實(shí)例并且執(zhí)行postProcessBeanFactory與postProcessBeanDefinitionRegistry方法2023-01-01詳解Spring Cloud Gateway 數(shù)據(jù)庫(kù)存儲(chǔ)路由信息的擴(kuò)展方案
這篇文章主要介紹了詳解Spring Cloud Gateway 數(shù)據(jù)庫(kù)存儲(chǔ)路由信息的擴(kuò)展方案,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-11-11Java設(shè)計(jì)模式之中介者模式(Mediator Pattern)簡(jiǎn)介
這篇文章主要介紹了Java設(shè)計(jì)模式之中介者模式(Mediator Pattern),需要的朋友可以參考下2014-07-07Spring boot2X負(fù)載均衡和反向代理實(shí)現(xiàn)過(guò)程解析
這篇文章主要介紹了Spring boot2X負(fù)載均衡和反向代理實(shí)現(xiàn)過(guò)程解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-12-12mybatis報(bào)錯(cuò)元素內(nèi)容必須由格式正確的字符數(shù)據(jù)或標(biāo)記組成異常的解決辦法
今天小編就為大家分享一篇關(guān)于mybatis查詢(xún)出錯(cuò)解決辦法,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧2018-12-12