springboot動態切換數據源

1、創建一個springboot項目,導入依賴(3.3.0)
 <dependency><groupId>com.baomidou</groupId><artifactId>dynamic-datasource-spring-boot-starter</artifactId><version>3.6.1</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>3.0.3</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId></dependency><!--Druid連接池--><dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><version>1.2.3</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId><scope>runtime</scope><optional>true</optional></dependency><dependency><groupId>com.mysql</groupId><artifactId>mysql-connector-j</artifactId><scope>runtime</scope></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency></dependencies>
2、實現ThreadLocal

創建一個類用于實現ThreadLocal,主要是通過get,set,remove方法來獲取、設置、刪除當前線程對應的數據源。

public class DataSourceContextHolder {//此類提供線程局部變量。這些變量不同于它們的正常對應關系是每個線程訪問一個線程(通過get、set方法),有自己的獨立初始化變量的副本。private static final ThreadLocal<String> DATASOURCE_HOLDER = new ThreadLocal<>();/*** 設置數據源* @param dataSourceName 數據源名稱*/public static void setDataSource(String dataSourceName){DATASOURCE_HOLDER.set(dataSourceName);}/*** 獲取當前線程的數據源* @return 數據源名稱*/public static String getDataSource(){return DATASOURCE_HOLDER.get();}/*** 刪除當前數據源*/public static void removeDataSource(){DATASOURCE_HOLDER.remove();}
}
3、實現AbstractRoutingDataSource

定義一個動態數據源類實現AbstractRoutingDataSource,通過determineCurrentLookupKey方法與上述實現的ThreadLocal類中的get方法進行關聯,實現動態切換數據源。

/*** @description: 實現動態數據源,根據AbstractRoutingDataSource路由到不同數據源中* @date: 2023/7/27 11:18**/
public class DynamicDataSource extends AbstractRoutingDataSource {public DynamicDataSource(DataSource defaultDataSource,Map<Object, Object> targetDataSources){super.setDefaultTargetDataSource(defaultDataSource);super.setTargetDataSources(targetDataSources);}@Overrideprotected Object determineCurrentLookupKey() {return DataSourceContextHolder.getDataSource();}
}

上述代碼中,還實現了一個動態數據源類的構造方法,主要是為了設置默認數據源,以及以Map保存的各種目標數據源。其中Map的key是設置的數據源名稱,value則是對應的數據源(DataSource)。

4、配置數據庫

application.yml中配置數據庫信息:

#設置數據源
spring:datasource:type: com.alibaba.druid.pool.DruidDataSourcedruid:master:url: jdbc:mysql://xxxxxx:3306/test1?characterEncoding=utf-8&allowMultiQueries=true&zeroDateTimeBehavior=convertToNull&useSSL=falseusername: rootpassword: 123456driver-class-name: com.mysql.cj.jdbc.Driverslave:url: jdbc:mysql://xxxxx:3306/test2?characterEncoding=utf-8&allowMultiQueries=true&zeroDateTimeBehavior=convertToNull&useSSL=falseusername: rootpassword: 123456driver-class-name: com.mysql.cj.jdbc.Driverinitial-size: 15min-idle: 15max-active: 200max-wait: 60000time-between-eviction-runs-millis: 60000min-evictable-idle-time-millis: 300000validation-query: ""test-while-idle: truetest-on-borrow: falsetest-on-return: falsepool-prepared-statements: falseconnection-properties: false
mybatis:mapper-locations: mappers/**/*.xmlconfiguration:map-underscore-to-camel-case: truelog-impl: org.apache.ibatis.logging.stdout.StdOutImpl

配置類

/*** @description: 設置數據源* @date: 2023/7/27 11:34**/
@Configuration
public class DateSourceConfig {@Bean@ConfigurationProperties("spring.datasource.druid.master")public DataSource masterDataSource(){return DruidDataSourceBuilder.create().build();}@Bean@ConfigurationProperties("spring.datasource.druid.slave")public DataSource slaveDataSource(){return DruidDataSourceBuilder.create().build();}@Bean(name = "dynamicDataSource")@Primarypublic DynamicDataSource createDynamicDataSource(){Map<Object,Object> dataSourceMap = new HashMap<>();DataSource defaultDataSource = masterDataSource();dataSourceMap.put("master",defaultDataSource);dataSourceMap.put("slave",slaveDataSource());return new DynamicDataSource(defaultDataSource,dataSourceMap);}
}

通過配置類,將配置文件中的配置的數據庫信息轉換成datasource,并添加到DynamicDataSource中,同時通過@Bean將DynamicDataSource注入Spring中進行管理,后期在進行動態數據源添加時,會用到。

5、測試
@GetMapping("/getData.do/{datasourceName}")
public String getMasterData(@PathVariable("datasourceName") String datasourceName){DataSourceContextHolder.setDataSource(datasourceName);TestUser testUser = testUserMapper.selectOne(null);DataSourceContextHolder.removeDataSource();return testUser.getUserName();
}

通過執行結果,我們看到傳遞不同的數據源名稱,查詢對應的數據庫是不一樣的,返回結果也不一樣。
在上述代碼中,我們看到DataSourceContextHolder.setDataSource(datasourceName); 來設置了當前線程需要查詢的數據庫,通過DataSourceContextHolder.removeDataSource(); 來移除當前線程已設置的數據源。

注:啟動程序時,小伙伴不要忘記將SpringBoot自動添加數據源進行排除哦,否則會報循環依賴問題。

@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
6、優化調整(注解切換數據源)

在上述中,雖然已經實現了動態切換數據源,但是我們會發現如果涉及到多個業務進行切換數據源的話,我們就需要在每一個實現類中添加這一段代碼。
說到這有小伙伴應該就會想到使用注解來進行優化,接下來我們來實現一下。

6.1 定義注解

我們就用mybatis動態數據源切換的注解:DS,代碼如下:

@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface DS {String value() default "master";
}
6、2 實現aop
@Aspect
@Component
@Slf4j
public class DSAspect {    @Pointcut("@annotation(com.jiashn.dynamic_datasource.dynamic.aop.DS)")public void dynamicDataSource(){}@Around("dynamicDataSource()")public Object datasourceAround(ProceedingJoinPoint point) throws Throwable {MethodSignature signature = (MethodSignature)point.getSignature();Method method = signature.getMethod();DS ds = method.getAnnotation(DS.class);if (Objects.nonNull(ds)){DataSourceContextHolder.setDataSource(ds.value());}try {return point.proceed();} finally {DataSourceContextHolder.removeDataSource();}}
}

代碼使用了@Around,通過ProceedingJoinPoint獲取注解信息,拿到注解傳遞值,然后設置當前線程的數據源

6、4 測試
@GetMapping("/getMasterData.do")
public String getMasterData(){TestUser testUser = testUserMapper.selectOne(null);return testUser.getUserName();
}
@GetMapping("/getSlaveData.do")
@DS("slave")
public String getSlaveData(){TestUser testUser = testUserMapper.selectOne(null);return testUser.getUserName();
}

注意也可以同時操作多個數據源,只需要在相應方法上添加 @DS注解就可以,(service)
由于@DS中設置的默認值是:master,因此在調用主數據源時,可以不用進行添加。

通過執行結果,我們通過@DS也進行了數據源的切換,實現了Mybatis動態切換數據源中的通過注解切換數據源的方式。

第二種方式使用第三方框架,dynamic-datasource多數源組件
<dependency><groupId>com.baomidou</groupId><artifactId>dynamic-datasource-spring-boot-starter</artifactId><version>3.6.1</version></dependency>
配置數據源
spring:application:name: DynamicSourceDatadatasource:dynamic:#??????????????,?????masterprimary: master#???????,??false. true?????????????,false???????strict: falsedatasource:master:url: jdbc:mysql://192.168.64.140:3306/datasource1?serverTimezone=UTC&useUnicode=true&characterEncoding=UTF8&useSSL=falseusername: rootpassword: 123456driver-class-name: com.mysql.cj.jdbc.Driverslave_1:url: jdbc:mysql://192.168.64.136:3306/datasource2?serverTimezone=UTC&useUnicode=true&characterEncoding=UTF8&useSSL=falseusername: rootpassword: 123456driver-class-name: com.mysql.cj.jdbc.Drivercache:type: redisdata:redis:host: localhostport: 6379#password: 123456database: 0 #????0????jedis:#Redis?????pool:max-active: 8 #?????max-wait: 1ms #???????????max-idle: 4 #???????????min-idle: 0 #???????????
mybatis:mapper-locations: mappers/**/*.xmlconfiguration:map-underscore-to-camel-case: truelog-impl: org.apache.ibatis.logging.stdout.StdOutImpl
測試使用
@RestController
@RequestMapping("/friend")
public class FriendController {@Autowiredprivate FriendService friendService;//  主庫查詢@PostMapping("/find")public List<Friend> getListDatas() {return friendService.findListDatas();}/*** 注意:@DS("dsName") dsName可以為組名也可以為具體某個庫的名稱* 沒有@DS  默認數據源* 3.使用 @DS 切換數據源。* @DS 可以注解在方法上或類上,同時存在就近原則 方法上注解 優先于 類上注解* @return*///   操作從庫@DS("slave_1")@PostMapping("/find1")public List<Friend> getListDatas1() {return friendService.findListDatas();}// 主從庫同時操作@PostMapping("/find2")@DSTransactionalpublic List<Friend> getListDatas2() {
//        操作主庫數據List<Friend> masters = friendService.findMastDatas();
//        操作從庫數據List<Friend> salves = friendService.findSlaveDatas();masters.addAll(salves);System.out.println(masters);return masters;}
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/21126.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/21126.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/21126.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

滲透測試靶機----FirstLeads_1.3

滲透測試靶機----FirstLeads_1.3 啟動靶機&#xff0c;掃描ip&#xff0c;平平無奇 可以看出&#xff0c;這里是存在139這個主機的&#xff0c;這就是掃描出來的靶機ip繼續探測端口及其他信息 發現這里只開啟了80 端口 嘗試一些基本目錄&#xff0c;發現存在robot.txt文件目錄…

集成算法:Bagging模型、AdaBoost模型和Stacking模型

概述 目的&#xff1a;讓機器學習效果更好&#xff0c;單個不行&#xff0c;集成多個 集成算法 Bagging&#xff1a;訓練多個分類器取平均 f ( x ) 1 / M ∑ m 1 M f m ( x ) f(x)1/M\sum^M_{m1}{f_m(x)} f(x)1/M∑m1M?fm?(x) Boosting&#xff1a;從弱學習器開始加強&am…

插入排序以及希爾排序; 先學會插入,希爾會更簡單喔

1.前言 首先肯定是要學會插入排序再學習希爾排序會更簡單&#xff0c;因為代碼部分有很多相似之處&#xff1b;如果你覺得你很強&#xff0c;可以直接看希爾排序的講解。哈哈哈&#xff01;&#xff0c;每天進步一點點&#xff0c;和昨天的自己比 2.插入排序 讓我們先來看看…

鴻蒙Ability Kit(程序框架服務)【UIAbility組件與UI的數據同步】

UIAbility組件與UI的數據同步 基于當前的應用模型&#xff0c;可以通過以下幾種方式來實現UIAbility組件與UI之間的數據同步。 [使用EventHub進行數據通信]&#xff1a;在基類Context中提供了EventHub對象&#xff0c;可以通過發布訂閱方式來實現事件的傳遞。在事件傳遞前&am…

Rustdesk 自建服務器教程

一、環境 阿里云輕量服務器、debian11 系統 二、服務端搭建 2.1、開放防火墻指定端口 TCP(21115, 21116, 21117, 21118, 21119)UDP(21116) 2.2、安裝 rustdesk 服務器文件 在 github 下載頁https://github.com/rustdesk/rustdesk-server/releases/&#xff0c;下載 rustde…

【自撰寫,國際象棋入門】第1課、棋盤和棋子

第1課 棋盤和棋子 一、國際象棋的棋盤 國際象棋的棋盤為一8乘8的黑、白格相間的棋盤&#xff0c;8條豎線的編號分別為A-H&#xff0c;8條橫線的編號分別為1-8&#xff0c;在記譜時用豎線編號橫線編號的方式表示棋盤上的格子&#xff0c;例如a1格、h8格等.棋盤上有幾條重要的大…

c++程序員為什么要做自己的底層庫

五一期間&#xff0c;在家里翻到之前上學時候用的電腦和工作日志&#xff0c;粗略瀏覽一番&#xff0c;感慨10年歲月蹉跎&#xff0c;仍然沒有找到自己技術方向的“道”。遂有感而發&#xff0c;寫下此文。 算起來&#xff0c;接觸軟件開發也有10年時間了&#xff0c;最開始是…

Java——異常

1.什么是異常 將程序執行過程中發生的不正常行為稱為異常。 常見的異常有&#xff1a;算數異常&#xff0c;空指針異常&#xff0c;數組越界異常 每一種異常都有對應的類對齊描述 為了對每一種異常進行管理&#xff0c;Java內部實現了一個對異常的體系結構 1. Throwable&#x…

CS2游戲30萬掛箱賬號被封,飾品市場要變天

Steam游戲平臺上CS2的玩家在線人數常年位于第一位&#xff0c;即便偶爾會被爆款游戲擠下來&#xff0c;但一切都是暫時的。飾品交易作為CS2的重要組成部分&#xff0c;早已成為了維系游戲熱度的不二法門。可相對應的&#xff0c;各種掛箱子的工作室及個人也孕育而生。 但近來V社…

mysql多啟動

binary安裝&#xff1a; 1、redhat rpm 2、mysql rpm 3、mysql glibc source安裝&#xff1a; 1、5.1mysql(./configure && make && make install) 2、5.5mysql(cmake && make && make install) 單啟動&#xff1a; 1、安裝 tar xf xxx.tar…

【Docker學習】docker pull詳細說明

docker pull是我們經常用到的一個命令。我們使用一些官方鏡像&#xff0c;如MySql、Nginx等都需要用docker pull下載。不過不用的話&#xff0c;也可以。比如使用docker run&#xff0c;要是找不到鏡像&#xff0c;會自動下載。 命令&#xff1a; docker image pull 描述&am…

Uniapp寫一個簡單的商品瀑布流界面+商品詳情

最終效果&#xff1a; 整體內容比較簡單&#xff0c;參考了一篇瀑布流文章和一篇商品詳情文章隨便修改整了下&#xff0c;主要是給想做這方便面的新人一個簡單邏輯的展示&#xff08;其實我也是第一次寫這個emmm&#xff09; 一.組件下載&#xff1a; uni-icon uni-goods-nav…

什么是ACP?

前言 ACP指的是應用程序控制平面&#xff0c;是微服務架構中的一個關鍵組成部分。它負責管理微服務架構中的各個微服務&#xff0c;包括服務發現和注冊、負載均衡、服務路由、熔斷和降級、配置管理等方面的功能。 A&#xff1a;可用性 所有請求都有響應。C&#xff1a;強一致…

[DDR5 Jedec 3-4] 模式寄存器 Mode Register MRR/MRW

依公知及經驗整理,原創保護,禁止轉載。 專欄 《深入理解DDR》 1. 概念 模式寄存器用于定義各種操作模式。在初始化過程中,可以通過重新執行MRS命令來更改模式寄存器的內容。即使用戶只想修改模式寄存器變量的一個子集,在發出MRS命令時也必須編程所有變量。 只有當所有ban…

C語言案例-輸入任意三個數,按從大到小的順序輸出.

目錄 問題待續、更新中 問題 輸入任意三個數,按從大到小的順序輸出. 最大值 3數&#xff0c;重新排序輸出 輸出數據if來&#xff0c;ab ac bc比&#xff0c;比中里面交換值&#xff0c;輸出abc時為降序 代碼如下: #include <stdio.h> void main() {int a,b,c,t;printf(&…

現實殘酷!存款百萬只是少數人的游戲,普通家庭能存多少?

近期&#xff0c;網絡上掀起了一股關于普通家庭終身存款上限的熱烈討論。一位網友通過簡單的算術方式提出了一個假設&#xff1a;如果一對夫妻每年收入15萬&#xff0c;并成功將6萬存入銀行&#xff0c;那么從25歲步入社會至60歲退休&#xff0c;他們理論上能積累到210萬的存款…

從0開發一個Chrome插件:Manifest 文件詳解

前言 這是《從0開發一個Chrome插件》系列的第六篇文章,本系列教你如何從0去開發一個Chrome插件,每篇文章都會好好打磨,寫清楚我在開發過程遇到的問題,還有開發經驗和技巧。 專欄: 從0開發一個Chrome插件:什么是Chrome插件?從0開發一個Chrome插件:開發Chrome插件的必要…

C++知識點總結(36):二分進階練習

二分答案練習 一、憤怒的羊駝題目描述輸入描述輸出描述樣例1提示參考答案 二、偷吃西瓜題目描述輸入描述輸出描述樣例1提示參考答案 三、丟沙包題目描述輸入描述輸出描述樣例1提示參考答案 四、木材加工題目描述輸入描述輸出描述樣例1提示參考答案 五、路標設置題目描述輸入描述…

Go語言之GORM框架(四)——預加載,關聯標簽與多態關聯,自定義數據類型與事務(完結篇)

前言 本來是想著寫多表關系的&#xff0c;不過寫了一半發現重復的部分太多了&#xff0c;想了想與其做一些重復性工作&#xff0c;不如把一些當時覺得抽象的東西記錄一下&#xff0c;就當用一篇雜記完成專欄的最后一篇文章吧。 預加載 簡單示例 預加載主要用于在多表關系中…

谷歌瀏覽器的平替,內置開掛神器,我已愛不釋手!

油猴瀏覽器正式版是一款基于谷歌Chromium源碼開發的瀏覽器&#xff0c;它集成了集成了強大的油猴擴展&#xff08;Tampermonkey&#xff09;&#xff0c;使得用戶可以輕松安裝各種腳本&#xff0c;從而增強網頁瀏覽體驗。提供了一個更加個性化和高效的瀏覽體驗。 油猴擴展&…