色哟哟视频在线观看-色哟哟视频在线-色哟哟欧美15最新在线-色哟哟免费在线观看-国产l精品国产亚洲区在线观看-国产l精品国产亚洲区久久

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
电子发烧友
开通电子发烧友VIP会员 尊享10大特权
海量资料免费下载
精品直播免费看
优质内容免费畅学
课程9折专享价
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

SpringBoot實現動態切換數據源

jf_ro2CN3Fa ? 來源:稀土掘金技術社區 ? 2023-12-08 10:53 ? 次閱讀
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

最近在做業務需求時,需要從不同的數據庫中獲取數據然后寫入到當前數據庫中,因此涉及到切換數據源問題。本來想著使用Mybatis-plus中提供的動態數據源SpringBoot的starter:dynamic-datasource-spring-boot-starter來實現。

結果引入后發現由于之前項目環境問題導致無法使用。然后研究了下數據源切換代碼,決定自己采用ThreadLocal+AbstractRoutingDataSource來模擬實現dynamic-datasource-spring-boot-starter中線程數據源切換。

1 簡介

上述提到了ThreadLocal和AbstractRoutingDataSource,我們來對其進行簡單介紹下。

ThreadLocal:想必大家必不會陌生,全稱:thread local variable。主要是為解決多線程時由于并發而產生數據不一致問題。ThreadLocal為每個線程提供變量副本,確保每個線程在某一時間訪問到的不是同一個對象,這樣做到了隔離性,增加了內存,但大大減少了線程同步時的性能消耗,減少了線程并發控制的復雜程度。

ThreadLocal作用:在一個線程中共享,不同線程間隔離

ThreadLocal原理:ThreadLocal存入值時,會獲取當前線程實例作為key,存入當前線程對象中的Map中。

AbstractRoutingDataSource:根據用戶定義的規則選擇當前的數據源,

作用:在執行查詢之前,設置使用的數據源,實現動態路由的數據源,在每次數據庫查詢操作前執行它的抽象方法determineCurrentLookupKey(),決定使用哪個數據源。

2 代碼實現

程序環境:

SpringBoot2.4.8

Mybatis-plus3.2.0

Druid1.2.6

lombok1.18.20

commons-lang3 3.10

2.1 實現ThreadLocal

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

/**
*@author:jiangjs
*@description:
*@date:2023/7/2711:21
**/
publicclassDataSourceContextHolder{
//此類提供線程局部變量。這些變量不同于它們的正常對應關系是每個線程訪問一個線程(通過get、set方法),有自己的獨立初始化變量的副本。
privatestaticfinalThreadLocalDATASOURCE_HOLDER=newThreadLocal<>();

/**
*設置數據源
*@paramdataSourceName數據源名稱
*/
publicstaticvoidsetDataSource(StringdataSourceName){
DATASOURCE_HOLDER.set(dataSourceName);
}

/**
*獲取當前線程的數據源
*@return數據源名稱
*/
publicstaticStringgetDataSource(){
returnDATASOURCE_HOLDER.get();
}

/**
*刪除當前數據源
*/
publicstaticvoidremoveDataSource(){
DATASOURCE_HOLDER.remove();
}

}

2.2 實現AbstractRoutingDataSource

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

/**
*@author:jiangjs
*@description:實現動態數據源,根據AbstractRoutingDataSource路由到不同數據源中
*@date:2023/7/2711:18
**/
publicclassDynamicDataSourceextendsAbstractRoutingDataSource{

publicDynamicDataSource(DataSourcedefaultDataSource,MaptargetDataSources){
super.setDefaultTargetDataSource(defaultDataSource);
super.setTargetDataSources(targetDataSources);
}

@Override
protectedObjectdetermineCurrentLookupKey(){
returnDataSourceContextHolder.getDataSource();
}
}

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

2.3 配置數據庫

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

#設置數據源
spring:
datasource:
type:com.alibaba.druid.pool.DruidDataSource
druid:
master:
url:jdbc//xxxxxx:3306/test1?characterEncoding=utf-8&allowMultiQueries=true&zeroDateTimeBehavior=convertToNull&useSSL=false
username:root
password:123456
driver-class-name:com.mysql.cj.jdbc.Driver
slave:
url:jdbc//xxxxx:3306/test2?characterEncoding=utf-8&allowMultiQueries=true&zeroDateTimeBehavior=convertToNull&useSSL=false
username:root
password:123456
driver-class-name:com.mysql.cj.jdbc.Driver
initial-size:15
min-idle:15
max-active:200
max-wait:60000
time-between-eviction-runs-millis:60000
min-evictable-idle-time-millis:300000
validation-query:""
test-while-idle:true
test-on-borrow:false
test-on-return:false
pool-prepared-statements:false
connection-properties:false
/**
*@author:jiangjs
*@description:設置數據源
*@date:2023/7/2711:34
**/
@Configuration
publicclassDateSourceConfig{

@Bean
@ConfigurationProperties("spring.datasource.druid.master")
publicDataSourcemasterDataSource(){
returnDruidDataSourceBuilder.create().build();
}

@Bean
@ConfigurationProperties("spring.datasource.druid.slave")
publicDataSourceslaveDataSource(){
returnDruidDataSourceBuilder.create().build();
}

@Bean(name="dynamicDataSource")
@Primary
publicDynamicDataSourcecreateDynamicDataSource(){
MapdataSourceMap=newHashMap<>();
DataSourcedefaultDataSource=masterDataSource();
dataSourceMap.put("master",defaultDataSource);
dataSourceMap.put("slave",slaveDataSource());
returnnewDynamicDataSource(defaultDataSource,dataSourceMap);
}

}

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

2.4 測試

在主從兩個測試庫中,分別添加一張表test_user,里面只有一個字段user_name。

createtabletest_user(
user_namevarchar(255)notnullcomment'用戶名'
)

在主庫添加信息:

insertintotest_user(user_name)value('master');

從庫中添加信息:

insertintotest_user(user_name)value('slave');

我們創建一個getData的方法,參數就是需要查詢數據的數據源名稱。

@GetMapping("/getData.do/{datasourceName}")
publicStringgetMasterData(@PathVariable("datasourceName")StringdatasourceName){
DataSourceContextHolder.setDataSource(datasourceName);
TestUsertestUser=testUserMapper.selectOne(null);
DataSourceContextHolder.removeDataSource();
returntestUser.getUserName();
}

其他的Mapper和實體類大家自行實現。

執行結果:

1、傳遞master時:

圖片

2、傳遞slave時:

圖片

通過執行結果,我們看到傳遞不同的數據源名稱,查詢對應的數據庫是不一樣的,返回結果也不一樣。

在上述代碼中,我們看到DataSourceContextHolder.setDataSource(datasourceName); 來設置了當前線程需要查詢的數據庫,通過DataSourceContextHolder.removeDataSource(); 來移除當前線程已設置的數據源。使用過Mybatis-plus動態數據源的小伙伴,應該還記得我們在使用切換數據源時會使用到DynamicDataSourceContextHolder.push(String ds); 和DynamicDataSourceContextHolder.poll(); 這兩個方法,翻看源碼我們會發現其實就是在使用ThreadLocal時使用了棧,這樣的好處就是能使用多數據源嵌套,這里就不帶大家實現了,有興趣的小伙伴可以看看Mybatis-plus中動態數據源的源碼。

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

@SpringBootApplication(exclude=DataSourceAutoConfiguration.class)

2.5 優化調整

2.5.1 注解切換數據源

在上述中,雖然已經實現了動態切換數據源,但是我們會發現如果涉及到多個業務進行切換數據源的話,我們就需要在每一個實現類中添加這一段代碼。

說到這有小伙伴應該就會想到使用注解來進行優化,接下來我們來實現一下。

2.5.1.1 定義注解

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

/**
*@author:jiangjs
*@description:
*@date:2023/7/2714:39
**/
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public@interfaceDS{
Stringvalue()default"master";
}

2.5.1.2 實現aop

@Aspect
@Component
@Slf4j
publicclassDSAspect{

@Pointcut("@annotation(com.jiashn.dynamic_datasource.dynamic.aop.DS)")
publicvoiddynamicDataSource(){}

@Around("dynamicDataSource()")
publicObjectdatasourceAround(ProceedingJoinPointpoint)throwsThrowable{
MethodSignaturesignature=(MethodSignature)point.getSignature();
Methodmethod=signature.getMethod();
DSds=method.getAnnotation(DS.class);
if(Objects.nonNull(ds)){
DataSourceContextHolder.setDataSource(ds.value());
}
try{
returnpoint.proceed();
}finally{
DataSourceContextHolder.removeDataSource();
}
}
}

代碼使用了@Around,通過ProceedingJoinPoint獲取注解信息,拿到注解傳遞值,然后設置當前線程的數據源。對aop不了解的小伙伴可以自行google或百度。

2.5.1.3 測試

添加兩個測試方法:

@GetMapping("/getMasterData.do")
publicStringgetMasterData(){
TestUsertestUser=testUserMapper.selectOne(null);
returntestUser.getUserName();
}

@GetMapping("/getSlaveData.do")
@DS("slave")
publicStringgetSlaveData(){
TestUsertestUser=testUserMapper.selectOne(null);
returntestUser.getUserName();
}

由于@DS中設置的默認值是:master,因此在調用主數據源時,可以不用進行添加。

執行結果:

1、調用getMasterData.do方法:

圖片

2、調用getSlaveData.do方法:

圖片

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

2.5.2 動態添加數據源

業務場景 :有時候我們的業務會要求我們從保存有其他數據源的數據庫表中添加這些數據源,然后再根據不同的情況切換這些數據源。

因此我們需要改造下DynamicDataSource來實現動態加載數據源。

2.5.2.1 數據源實體

/**
*@author:jiangjs
*@description:數據源實體
*@date:2023/7/2715:55
**/
@Data
@Accessors(chain=true)
publicclassDataSourceEntity{

/**
*數據庫地址
*/
privateStringurl;
/**
*數據庫用戶名
*/
privateStringuserName;
/**
*密碼
*/
privateStringpassWord;
/**
*數據庫驅動
*/
privateStringdriverClassName;
/**
*數據庫key,即保存Map中的key
*/
privateStringkey;
}

實體中定義數據源的一般信息,同時定義一個key用于作為DynamicDataSource中Map中的key。

2.5.2.2 修改DynamicDataSource

/**
*@author:jiangjs
*@description:實現動態數據源,根據AbstractRoutingDataSource路由到不同數據源中
*@date:2023/7/2711:18
**/
@Slf4j
publicclassDynamicDataSourceextendsAbstractRoutingDataSource{

privatefinalMaptargetDataSourceMap;

publicDynamicDataSource(DataSourcedefaultDataSource,MaptargetDataSources){
super.setDefaultTargetDataSource(defaultDataSource);
super.setTargetDataSources(targetDataSources);
this.targetDataSourceMap=targetDataSources;
}

@Override
protectedObjectdetermineCurrentLookupKey(){
returnDataSourceContextHolder.getDataSource();
}

/**
*添加數據源信息
*@paramdataSources數據源實體集合
*@return返回添加結果
*/
publicvoidcreateDataSource(ListdataSources){
try{
if(CollectionUtils.isNotEmpty(dataSources)){
for(DataSourceEntityds:dataSources){
//校驗數據庫是否可以連接
Class.forName(ds.getDriverClassName());
DriverManager.getConnection(ds.getUrl(),ds.getUserName(),ds.getPassWord());
//定義數據源
DruidDataSourcedataSource=newDruidDataSource();
BeanUtils.copyProperties(ds,dataSource);
//申請連接時執行validationQuery檢測連接是否有效,這里建議配置為TRUE,防止取到的連接不可用
dataSource.setTestOnBorrow(true);
//建議配置為true,不影響性能,并且保證安全性。
//申請連接的時候檢測,如果空閑時間大于timeBetweenEvictionRunsMillis,執行validationQuery檢測連接是否有效。
dataSource.setTestWhileIdle(true);
//用來檢測連接是否有效的sql,要求是一個查詢語句。
dataSource.setValidationQuery("select1");
dataSource.init();
this.targetDataSourceMap.put(ds.getKey(),dataSource);
}
super.setTargetDataSources(this.targetDataSourceMap);
//將TargetDataSources中的連接信息放入resolvedDataSources管理
super.afterPropertiesSet();
returnBoolean.TRUE;
}
}catch(ClassNotFoundException|SQLExceptione){
log.error("---程序報錯---:{}",e.getMessage());
}
returnBoolean.FALSE;
}

/**
*校驗數據源是否存在
*@paramkey數據源保存的key
*@return返回結果,true:存在,false:不存在
*/
publicbooleanexistsDataSource(Stringkey){
returnObjects.nonNull(this.targetDataSourceMap.get(key));
}
}

在改造后的DynamicDataSource中,我們添加可以一個 private final Map targetDataSourceMap,這個map會在添加數據源的配置文件時將創建的Map數據源信息通過DynamicDataSource構造方法進行初始賦值,即:DateSourceConfig類中的createDynamicDataSource()方法中。

同時我們在該類中添加了一個createDataSource方法,進行數據源的創建,并添加到map中,再通過super.setTargetDataSources(this.targetDataSourceMap) ;進行目標數據源的重新賦值。

2.5.2.3 動態添加數據源

上述代碼已經實現了添加數據源的方法,那么我們來模擬通過從數據庫表中添加數據源,然后我們通過調用加載數據源的方法將數據源添加進數據源Map中。

在主數據庫中定義一個數據庫表,用于保存數據庫信息。

createtabletest_db_info(
idintauto_incrementprimarykeynotnullcomment'主鍵Id',
urlvarchar(255)notnullcomment'數據庫URL',
usernamevarchar(255)notnullcomment'用戶名',
passwordvarchar(255)notnullcomment'密碼',
driver_class_namevarchar(255)notnullcomment'數據庫驅動'
namevarchar(255)notnullcomment'數據庫名稱'
)

為了方便,我們將之前的從庫錄入到數據庫中,修改數據庫名稱。

insertintotest_db_info(url,username,password,driver_class_name,name)
value('jdbc//xxxxx:3306/test2?characterEncoding=utf-8&allowMultiQueries=true&zeroDateTimeBehavior=convertToNull&useSSL=false',
'root','123456','com.mysql.cj.jdbc.Driver','add_slave')

數據庫表對應的實體、mapper,小伙伴們自行添加。

啟動SpringBoot時添加數據源:

/**
*@author:jiangjs
*@description:
*@date:2023/7/2716:56
**/
@Component
publicclassLoadDataSourceRunnerimplementsCommandLineRunner{
@Resource
privateDynamicDataSourcedynamicDataSource;
@Resource
privateTestDbInfoMappertestDbInfoMapper;
@Override
publicvoidrun(String...args)throwsException{
ListtestDbInfos=testDbInfoMapper.selectList(null);
if(CollectionUtils.isNotEmpty(testDbInfos)){
Listds=newArrayList<>();
for(TestDbInfotestDbInfo:testDbInfos){
DataSourceEntitysourceEntity=newDataSourceEntity();
BeanUtils.copyProperties(testDbInfo,sourceEntity);
sourceEntity.setKey(testDbInfo.getName());
ds.add(sourceEntity);
}
dynamicDataSource.createDataSource(ds);
}
}
}

經過上述SpringBoot啟動后,已經將數據庫表中的數據添加到動態數據源中,我們調用之前的測試方法,將數據源名稱作為參數傳入看看執行結果。

2.5.2.4 測試

圖片

通過測試我們發現數據庫表中的數據庫被動態加入了數據源中,小伙伴可以愉快地隨意添加數據源了。

好了,今天就跟大家嘮叨到這,希望我的叨叨讓大家對于動態切換數據源的方式能夠有更深地理解。

審核編輯:湯梓紅

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • 內存
    +關注

    關注

    8

    文章

    3117

    瀏覽量

    75120
  • 數據源
    +關注

    關注

    1

    文章

    65

    瀏覽量

    9897
  • SpringBoot
    +關注

    關注

    0

    文章

    175

    瀏覽量

    359

原文標題:SpringBoot 實現動態切換數據源,這樣做才更優雅!

文章出處:【微信號:芋道源碼,微信公眾號:芋道源碼】歡迎添加關注!文章轉載請注明出處。

收藏 0人收藏
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

    評論

    相關推薦
    熱點推薦

    Mybatis 攔截器實現數據源內多數據切換

    數據庫 現在需要上線報表服務來查詢所有數據庫中的數據進行統計,那么現在的問題來了,該如何 滿足在配置一個數據源的情況下來查詢該數據源下不同
    的頭像 發表于 12-12 10:23 ?1306次閱讀

    LabView動態創建數據源的方法

    DSN(Data Source Name,數據源名)。LabSQL與數據庫之間的連接就是建立在DSN 基礎之上的。但是這種過于麻煩,在生成操作程序時不便于安裝,于是需要一種可以在LabView中直接創建數據源的方法。通過資料查證
    發表于 09-23 01:53

    【測試之王LabVIEW】注冊表應用一:動態注冊數據源

    Source Name,數據源名)。LabSQL與數據庫之間的連接就是建立在DSN 基礎之上的。現在通過修改注冊表的方式,來實現動態創建數據源
    發表于 10-31 14:27

    ODBC數據源的建立

    我找不到數據源這個選項啊,怎么辦?
    發表于 04-21 11:39

    如何用現成的軸承數據動態數據源進行動態模擬測試系統

    如何用現成的軸承數據動態數據源進行動態模擬測試系統
    發表于 03-11 15:39

    SpringBoot中的Druid介紹

    SpringBoot中Druid數據源配置
    發表于 05-07 09:21

    SpringBoot項目多數據源配置數據

    SpringBoot項目多數據源配置
    發表于 06-05 09:51

    數據源配置工具

    odbc_for_access.exe1、可直接配置數據源。2、可直接創建Access數據庫文件。3、為數據庫配置密碼。
    發表于 07-01 16:22 ?4次下載

    Delphi教程之動態設置ODBC數據源

    Delphi教程之動態設置ODBC數據源,學習Delphi編程的資料。
    發表于 03-31 11:29 ?13次下載

    基于LDA主題模型進行數據源選擇方法

    聯邦搜索是從大規模深層網上獲取信息的一種重要技術。給定一個用戶查詢,聯邦搜索系統需要解決的一個主要問題是數據源選擇問題,即從海量數據源中選出一組最有可能返回相關結果的數據源。現有的數據源
    發表于 01-04 15:00 ?0次下載
    基于LDA主題模型進行<b class='flag-5'>數據源</b>選擇方法

    Deep Web數據源選擇和集成方法

    針對基于數據源質量選擇方法的數據源數據爬取時存在代價大、重復率高的問題,提出一種結合兩層選擇模型的Deep Web數據源選擇和集成方法。該方法根據
    發表于 02-09 15:24 ?0次下載
    Deep Web<b class='flag-5'>數據源</b>選擇和集成方法

    數據倉庫入門之創建數據源

    首先需要創建一個數據源,SSAS(分析服務)將利用數據源來連接數據庫。一、準備環境二、啟動SSDT,新建項目三、創建數據源
    發表于 02-24 14:48 ?2674次閱讀
    <b class='flag-5'>數據</b>倉庫入門之創建<b class='flag-5'>數據源</b>

    SpringBoot分布式事務的解決方案(JTA+Atomic+多數據源

    首先,到底啥是分布式事務呢,比如我們在執行一個業務邏輯的時候有兩步分別操作A數據源和B數據源,當我們在A數據源執行數據更改后,在B數據源執行
    的頭像 發表于 04-11 11:05 ?2079次閱讀

    SpringBoot數據源及事務解決方案

    當Spring容器創建AbstractRoutingDataSource對象時,通過調用afterPropertiesSet復制上述目標數據源。由此可見,一旦數據源實例對象創建完畢,業務無法再添加新的數據源
    的頭像 發表于 04-12 11:22 ?2740次閱讀

    weblogic修改數據源需要重啟嗎

    WebLogic是一款支持Java EE(Java Enterprise Edition)規范的應用服務器,通過WebLogic可進行數據源的配置和管理。在WebLogic中修改數據源是否需要重啟
    的頭像 發表于 12-05 16:09 ?1917次閱讀
    主站蜘蛛池模板: 天堂视频在线观看免费完整版 | 伦理在线影院伦理电影 | 日本无翼恶漫画大全优优漫画 | 广播电台在线收听 | 九九这里有精品 | 做你的爱人BD日本 | 伊人香蕉在线播放视频免费 | 国产欧美一本道无码 | 99久久久A片无码国产精 | 高清国产mv视频在线观看 | 亚洲精品国产在线网站 | 亚洲午夜精品久久久久久抢 | 九九精品视频一区二区三区 | 免费三级播放器 | 三级貂蝉艳史 在线观看 | 亚洲精品在线网址 | 国产在线不卡 | 97成人精品视频在线播放 | 69ZXX少妇内射无码 | 在线观看免费小视频 | 国产亚洲精品成人AV久久 | bbwvideoa欧美老妇 | 日本精品久久久久中文字幕 | 在线中文字幕 | 久久无码人妻中文国产 | 久久精品国产99欧美精品亚洲 | 欧美日韩亚洲综合2019 | 久久青青草视频在线观 | 伊人久久大香线蕉综合影 | 亚洲高清一区二区三区电影 | 免费毛片视频网站 | 无码一区二区在线欧洲 | 怪物高h粗暴无尽 | 日日啪在线影院百度 | 久久久精品日本一区二区三区 | 精品视频在线一区 | 国产永不无码精品AV永久 | 永久午夜福利视频一区在线观看 | 中文字幕精品视频在线 | 被窝国产理论一二三影院 | 人成片在线观看亚洲无遮拦 |

    電子發燒友

    中國電子工程師最喜歡的網站

    • 2931785位工程師會員交流學習
    • 獲取您個性化的科技前沿技術信息
    • 參加活動獲取豐厚的禮品