SpringBoot實現多數據源切換

1. 概述


隨著項目規模的擴大和業務需求的復雜化,單一數據源已經不能滿足實際開發中的需求。在許多情況下,我們需要同時操作多個數據庫,或者需要將不同類型的數據存儲在不同的數據庫中。這時,多數據源場景成為必不可少的解決方案。

市面上常見的多數據源實現方案如下:

  • 方案1:基于Spring框架提供的AbstractRoutingDataSource。

    • 優點: 簡單易用,支持動態切換數據源;適用于少量數據源情況。
    • 場景:適用于需要動態切換數據源,且數據庫較少的情況。
    • 文檔地址:
  • 方案2:使用MP提供的Dynamic-datasource多數據源框架。

    • 文檔地址:https://baomidou.com/guides/dynamic-datasource/#dynamic-datasource
  • 方案3:通過自定義注解在方法或類上指定數據源,實現根據注解切換數據源的功能。

    • 優點: 靈活性高,能夠精確地控制數據源切換;在代碼中直觀明了。
    • 場景: 適用于需要在代碼層面進行數據源切換,并對數據源切換有精細要求的情況。
  • 方案4:使用動態代理技術,在運行時動態切換數據源,實現多數據源的切換。

    • 優點: 靈活性高,支持在運行時動態切換數據源;適合對數據源切換的邏輯有特殊需求的情況。
    • 場景: 適用于需要在運行時動態決定數據源切換策略的情況。

2. 基于SpringBoot的多數據源實現方案


1、執行sql腳本:(分別創建兩個數據庫,里面都提供一張user表)

-- 創建數據庫ds1
CREATE DATABASE `ds1`;-- 使用ds1數據庫
USE ds1;-- 創建user表
CREATE TABLE `user` (`id` INT PRIMARY KEY AUTO_INCREMENT COMMENT '主鍵id',`username` VARCHAR(50) COMMENT '用戶名',`gender` TINYINT(1) COMMENT '性別:0男,1女'
);-- 向user表插入數據
INSERT INTO user (username, gender) VALUES 
('張三', 1),
('李四', 0),
('王五', 1);-- 創建數據庫ds2
CREATE DATABASE `ds2`;-- 使用ds2數據庫
USE ds2;-- 創建user表
CREATE TABLE `user` (`id` INT PRIMARY KEY AUTO_INCREMENT COMMENT '主鍵id',`username` VARCHAR(50) COMMENT '用戶名',`gender` TINYINT(1) COMMENT '性別:0男,1女'
);-- 向user表插入數據
INSERT INTO user (username, gender) VALUES 
('趙六', 1),
('陳七', 0),
('寶國', 1);

2、創建一個maven工程,向pom.xml中添加依賴:

<!--鎖定SpringBoot版本-->
<parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.7.10</version><relativePath/> <!-- lookup parent from repository -->
</parent><dependencies><!--jdbc起步依賴--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jdbc</artifactId>
</dependency><!--test起步依賴--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId></dependency><!--mysql驅動--><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.20</version></dependency><!--lombok--><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency><!--jdbc起步依賴--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jdbc</artifactId></dependency>
</dependencies>

3、編寫實體類:

package cn.aopmin.entity;import lombok.*;/*** 實體類** @author 白豆五* @since 2024/7/4*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class User {private Integer id;private String username;private Integer gender;
}

4、創建application.yml文件,配置數據源:

spring:#動態數據源配置datasource:ds1:driverClassName: com.mysql.cj.jdbc.DriverjdbcUrl: jdbc:mysql://localhost:3306/ds1?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&useSSL=falseusername: rootpassword: 123456ds2:driverClassName: com.mysql.cj.jdbc.DriverjdbcUrl: jdbc:mysql://localhost:3306/ds2?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&useSSL=falseusername: rootpassword: 123456logging:level:cn.aopmin: debug

5、編寫數據源配置類:

package cn.aopmin.config;import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;/*** 數據源配置類* 配置多數據源和動態數據源** @author 白豆五* @since 2024/7/4*/
@Configuration
public class DataSourceConfig {//定義數據源1@Bean("ds1")@ConfigurationProperties(prefix = "spring.datasource.ds1")public DataSource ds1() {return DataSourceBuilder.create().build();}//定義數據源2@Bean("ds2")@ConfigurationProperties(prefix = "spring.datasource.ds2")public DataSource ds2() {return DataSourceBuilder.create().build();}//定義動態數據源@Bean(name = "dataSource")public DataSource dynamicDataSource(@Qualifier("ds1") DataSource ds1,@Qualifier("ds2") DataSource ds2) {//1.定義數據源mapMap<Object, Object> targetDataSources = new HashMap<>();targetDataSources.put("ds1", ds1);targetDataSources.put("ds2", ds2);//2.實例化自定義的DynamicDataSource對象, 并設置數據源mapDynamicDataSource dynamicDataSource = new DynamicDataSource();dynamicDataSource.setTargetDataSources(targetDataSources);//3.設置默認數據源,未匹配上則使用默認數據源dynamicDataSource.setDefaultTargetDataSource(ds1);return dynamicDataSource;}// 通過JdbcTemplate	@Beanpublic JdbcTemplate jdbcTemplate(@Qualifier("dataSource") DataSource ds) {return new JdbcTemplate(ds);}
}

6、創建DynamicDataSource動態數據類:

package cn.aopmin.config;import cn.aopmin.common.DataSourceContextHolder;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;/*** AbstractRoutingDataSource(抽象的數據源路由器) 的基本原理是, 它維護了一個數據源的集合,每個數據源都有唯一的一個標識符* 當應用程序需要訪問數據庫的時候,AbstractRoutingDataSource會根據某種匹配規則(例如請求參數、用戶身份等)來選擇一個合適的數據源,* 并將請求轉發給這個數據源。*/
public class DynamicDataSource extends AbstractRoutingDataSource {/*** 獲取數據源名稱* @return*/@Overrideprotected Object determineCurrentLookupKey() {return DataSourceContextHolder.getDataSource();}
}

7、定義一個ThreadLocal工具類:

package cn.aopmin.common;/*** 使用ThreadLocal保存數據源名稱** @author 白豆五* @since 2024/7/4*/
public class DataSourceContextHolder {private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();// 將數據源名稱綁定到當前線程上public static void setDataSource(String dataSourceName) {contextHolder.set(dataSourceName);}// 獲取當前線程上的數據源名稱public static String getDataSource() {return contextHolder.get();}// 清除數據源名稱public static void clearDataSource() {contextHolder.remove();}
}

8、創建啟動類

package cn.aopmin;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;/*** 啟動類** @author 白豆五* @since 2024/7/3*/
@SpringBootApplication
public class Demo01Application {public static void main(String[] args) {SpringApplication.run(Demo01Application.class, args);}
}

9、創建UserService:

package cn.aopmin.service;import cn.aopmin.common.DataSourceContextHolder;
import cn.aopmin.entity.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;/*** @author 白豆五* @since 2024/7/4*/
@Service
public class UserService {@Autowiredprivate JdbcTemplate jdbcTemplate;public void insertDs1(User user) {try {// todo:自定義注解+SpringAop實現數據源的切換DataSourceContextHolder.setDataSource("ds1");String sql = "insert into user(username,gender) values(?,?)";jdbcTemplate.update(sql,user.getUsername(), user.getGender());} finally {DataSourceContextHolder.clearDataSource();}}public void insertDs2(User user) {try {DataSourceContextHolder.setDataSource("ds2");String sql = "insert into user(username,gender) values(?,?)";jdbcTemplate.update(sql,user.getUsername(), user.getGender());} finally {DataSourceContextHolder.clearDataSource();}}
}

10、編寫測試:

package cn.aopmin.service;import cn.aopmin.Demo01Application;
import cn.aopmin.entity.User;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;import javax.annotation.Resource;@SpringBootTest(classes = {Demo01Application.class})
public class UserServiceTest {@Resourceprivate UserService userService;@Testpublic void testInsertDs1() {User user = new User();user.setUsername("jack");user.setGender(0);user.setGender(1);userService.insertDs1(user);}@Testpublic void testInsertDs2() {User user = new User();user.setUsername("rose");user.setGender(1);userService.insertDs2(user);}
}

最終效果:

在這里插入圖片描述


3. 基于Dynamic-datasource實現方案


mp文檔:https://baomidou.com/guides/dynamic-datasource/#_top

1、創建SpringBoot工程,引入Dynamic-datasource依賴:

<dependency><groupId>com.baomidou</groupId><artifactId>dynamic-datasource-spring-boot-starter</artifactId><version>3.5.2</version>
</dependency>

2、配置數據源:

spring:#多數據源配置datasource:dynamic:primary: master #設置默認數據源strict: false #是否嚴格檢查動態數據源提供的數據庫名datasource:#數據源1master:url: jdbc:mysql:///ds1?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghaiusername: rootpassword: 123456driver-class-name: com.mysql.cj.jdbc.Driver#數據源2slave1:url: jdbc:mysql:///ds2?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghaiusername: rootpassword: 123456driver-class-name: com.mysql.cj.jdbc.Driver

3、實體類:

package cn.aopmin.entity;import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;/*** 實體類** @author 白豆五* @since 2024/7/4*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class User {private Integer id;private String username;private Integer gender;
}

4、業務類:

package cn.aopmin.service;import cn.aopmin.entity.User;
import com.baomidou.dynamic.datasource.annotation.DS;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;/*** 通過@DS注解切換數據源* @author 白豆五* @since 2024/7/4*/
@Service
// @DS("master") //不加@DS注解,會使用默認數據源
public class UserService {@Autowiredprivate JdbcTemplate jdbcTemplate;public void insertDs1(User user) {String sql = "insert into user(username,gender) values(?,?)";jdbcTemplate.update(sql,user.getUsername(), user.getGender());}@DS("slave1")public void insertDs2(User user) {String sql = "insert into user(username,gender) values(?,?)";jdbcTemplate.update(sql,user.getUsername(), user.getGender());}
}

4、測試類:

package cn.aopmin.service;import cn.aopmin.entity.User;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;import javax.annotation.Resource;@SpringBootTest
public class UserServiceTest2 {@Resourceprivate UserService userService;@Testpublic void testInsertDs1() {User user = new User();user.setUsername("jack");user.setGender(0);user.setGender(1);userService.insertDs1(user);}@Testpublic void testInsertDs2() {User user = new User();user.setUsername("rose");user.setGender(1);userService.insertDs2(user);}
}

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

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

相關文章

【CentOS7.6】docker部署EMQX教程,本地鏡像直接導入(附下載鏈接),沒法在云服務器上魔法拉取鏡像的快來

總覽 先把下載鏈接放在這里吧&#xff0c;這是 EMQX 的 tar 包&#xff0c;能夠直接導入 CentOS 的 docker&#xff1a; 鏈接&#xff1a;https://pan.baidu.com/s/1rSGSLoVvj83ai6d5oolg8Q?pwd0108 提取碼&#xff1a;0108 一、安裝配置教程 1.將 EMQX-latest.tar 包導入…

服務器重裝系統時數據丟失?有哪些方法可以避免

為了避免在服務器重裝系統時數據丟失&#xff0c;可以采取以下預防措施&#xff1a; 1. 數據備份&#xff1a;在重裝系統之前&#xff0c;備份所有重要的數據和配置文件。備份可以通過以下方式進行&#xff1a; - 使用外部存儲設備(如USB硬盤、NAS等)進行備份。 - 利用備份軟件…

學習成績總是上不去?中學生把握好這5個環節,助你提高成績

在中學時代&#xff0c;考試我們并不陌生。每隔一段時間&#xff0c;學校就會安排我們參加考試。學生時代&#xff0c;我們參加的考試有很多。對于中學生來說&#xff0c;考試成績是我們一直關心的事情。很多學生非常努力的學習&#xff0c;成績卻上不去。這是非常可惜的&#…

[圖解]企業應用架構模式2024新譯本講解19-數據映射器1

1 00:00:01,720 --> 00:00:03,950 下一個我們要講的就是 2 00:00:04,660 --> 00:00:07,420 數據映射器這個模式 3 00:00:09,760 --> 00:00:13,420 這個也是在數據源模式里面 4 00:00:13,430 --> 00:00:14,820 用得最廣泛的 5 00:00:16,250 --> 00:00:19,170…

【軟件工程中的噴泉模型及其優缺點】

文章目錄 一、噴泉模型是什么&#xff1f;二、噴泉模型的優點1. 靈活性和適應性2. 迭代開發3. 風險控制 三、噴泉模型的缺點1. 需求不明確性2. 可能造成資源浪費3. 需要良好的溝通與協作 一、噴泉模型是什么&#xff1f; 噴泉模型是一種迭代增量開發模型&#xff0c;其核心理念…

鏈篦機回轉窯球團生產工藝

生球在回轉窯氧化焙燒&#xff0c;回轉窯頭部設有燃燒器&#xff0c;燃料可以采用氣體、固體、液體。 來自環冷機一冷卻段的高溫廢氣作為二次風進入窯內參與燃燒&#xff0c;燒成成品球進入環冷機。 環冷機采用鼓風冷卻&#xff0c;熱風風箱分為四段&#xff1a; 一段氣體引至…

無人機有哪些關鍵技術?

一、控制技術 無人機的核心還是在控制上&#xff0c;飛控系統的可靠性、穩定性及可擴展性是其中重要的指標。可靠性上&#xff0c;除了器件選型之外&#xff0c;目前主要靠多余度來增加&#xff1b;穩定性主要體現在多場景下仍能保持良好的工作狀態&#xff0c;主要靠算法來進…

QML-各類布局

Colunm布局 Column{id:colspacing: 30Repeater{id:repmodel: ListModel{}Button{width: 100height: 50text: "btn"index}}//開始時候移動move: Transition {NumberAnimation { properties: "x,y"; easing.type: Easing.OutBounce }}//添加時變化add:Transi…

【Nginx】docker運行Nginx及配置

Nginx鏡像的獲取 直接從Docker Hub拉取Nginx鏡像通過Dockerfile構建Nginx鏡像后拉取 二者區別 主要區別在于定制化程度和構建過程的控制&#xff1a; 直接拉取Nginx鏡像&#xff1a; 簡便性&#xff1a;直接使用docker pull nginx命令可以快速拉取官方的Nginx鏡像。這個過程…

通透!手把教你如何從頭構建一個機器學習模型

目錄 1.業務理解 2.數據收集和準備 數據采集 探索性數據分析 (EDA) 和數據清理 特征選擇 3.建立機器學習模型 選擇正確的模型 分割數據 訓練模型 模型評估 4.模型優化 5.部署模型 今天我將帶領大家一步步的來構建一個機器學習模型。 我們將按照以下步驟開發客戶流失…

賽博解壓板

目錄 開頭程序程序的流程圖程序的解壓效果(暫無&#xff0c;但可以運行一下上面的代碼)結尾 開頭 大家好&#xff0c;我叫這是我58。今天&#xff0c;我們要看關于賽博解壓板的一些東西。 程序 #define _CRT_SECURE_NO_WARNINGS 1 #define ROW 6//ROW表示行數&#xff0c;可…

【ARM 常見匯編指令學習 7.1 -- LDRH 半字讀取指令】

請閱讀【嵌入式開發學習必備專欄】 文章目錄 LDRH 使用介紹LDRH&#xff08;Load Register Half-word&#xff09;總結 LDRH 使用介紹 在ARMv9架構中&#xff0c;匯編指令LDRH用于從內存中載入數據到寄存器的指令&#xff0c;下面將分別對它進行詳細介紹&#xff1a; LDRH&am…

【基礎算法】UE中實現輪播

本期作者&#xff1a;尼克 易知微3D引擎技術負責人 當前N 總數M 從0到M-1 從1到M 感謝閱讀&#xff0c;以上內容均由易知微3D引擎團隊原創設計&#xff0c;以及易知微版權所有&#xff0c;轉載請注明出處&#xff0c;違者必究&#xff0c;謝謝您的合作。申請轉載授權后臺回復【…

【WebKit屏幕方向API全解析】掌握現代Web應用的方向感應

標題&#xff1a;【WebKit屏幕方向API全解析】掌握現代Web應用的方向感應 WebKit作為許多現代瀏覽器的內核&#xff0c;提供了對HTML5和CSS3的廣泛支持&#xff0c;包括對屏幕方向的控制。屏幕方向API&#xff08;Screen Orientation API&#xff09;允許Web應用知道屏幕的方向…

左耳聽風_114_113_Go編程模式修飾器

你好&#xff0c;我是陳浩&#xff0c;我名多爾多house.之前呢我寫過一篇文章叫做python修飾器的函數式編程。 那這種模式呢可以很輕松的把一些函數啊裝配到另外一些函數上。 讓你的代碼呢更加簡單&#xff0c;也可以讓一些小功能性的代碼復用性更高。 讓代碼中的函數呢可以…

掌握XD數字設計:打造令人驚艷的用戶體驗

xd是adobe旗下一款主打UI界面設計-建立原型的軟件&#xff0c;它可以將wireframe、design、以及prototype等UI/UX設計流程整合到一個軟件中&#xff0c;算是一款與sketch對打的軟件。 與PS相比&#xff0c;在UI設計方面&#xff0c;Adobe XD有非常突出的3個優點&#xff1a;能…

從0到1手寫vue源碼

模版引擎 數組join法&#xff08;字符串&#xff09; es6反引號法&#xff08;模版字符串換行&#xff09; mustache (小胡子) 引入mustache 模版引擎的使用 mustache.render(templatestr,data) mustache.render 循環簡單數組 循環復雜數組 循環單項數組 數組的嵌套 musta…

江蘇徐州SAP代理商有哪些?怎么選擇?

在數字化浪潮席卷全球的今天&#xff0c;企業對于高效、智能的管理系統需求日益迫切。SAP作為全球領先的企業管理軟件解決方案提供商&#xff0c;其產品在市場上享有極高的聲譽。而在江蘇徐州&#xff0c;哲訊智能科技作為SAP的代理商&#xff0c;以其專業的技術實力和優質的服…

開源205W桌面充電器,140W+65W升降壓PD3.1快充模塊(2C+1A口),IP6557+IP6538

開源一個基于IP6557和IP6538芯片的205W升降壓快充模塊&#xff08;140W65W&#xff09;&#xff0c;其中一路C口支持PD3.1協議&#xff0c;最高輸出28V5A&#xff0c;另一路是A口C口&#xff0c;最高輸出65W&#xff08;20V3.25A&#xff09;&#xff0c;可搭配一個24V10A的開關…

代碼隨想錄算法訓練營第3天|LeetCode

203.移除鏈表元素 題目鏈接&#xff1a;203. 移除鏈表元素 - 力扣&#xff08;LeetCode&#xff09; 文檔鏈接&#xff1a;代碼隨想錄 (programmercarl.com) 視頻鏈接&#xff1a;手把手帶你學會操作鏈表 | LeetCode&#xff1a;203.移除鏈表元素_嗶哩嗶哩_bilibili 第一想法 …