一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|JavaScript|易語言|

服務器之家 - 編程語言 - Java教程 - springboot 動態數據源的實現方法(Mybatis+Druid)

springboot 動態數據源的實現方法(Mybatis+Druid)

2021-07-12 12:37時光沉舊了少年 Java教程

這篇文章主要介紹了springboot 動態數據源的實現方法(Mybatis+Druid),小編覺得挺不錯的,現在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧

spring多數據源實現的方式大概有2中,一種是新建多個mapperscan掃描不同包,另外一種則是通過繼承abstractroutingdatasource實現動態路由。今天作者主要基于后者做的實現,且方式1的實現比較簡單這里不做過多探討。

實現方式

方式1的實現(核心代碼):

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
@configuration
@mapperscan(basepackages = "com.goofly.test1", sqlsessiontemplateref = "test1sqlsessiontemplate")
public class datasource1config1 {
 
  @bean(name = "datasource1")
  @configurationproperties(prefix = "spring.datasource.test1")
  @primary
  public datasource testdatasource() {
    return datasourcebuilder.create().build();
  }
  // .....略
 
}
 
@configuration
@mapperscan(basepackages = "com.goofly.test2", sqlsessiontemplateref = "test1sqlsessiontemplate")
public class datasourceconfig2 {
 
  @bean(name = "datasource2")
  @configurationproperties(prefix = "spring.datasource.test2")
  @primary
  public datasource testdatasource() {
    return datasourcebuilder.create().build();
  }
  // .....略
 
}

方式2的實現(核心代碼):

?
1
2
3
4
5
6
7
8
9
public class dynamicroutingdatasource extends abstractroutingdatasource {
  private static final logger log = logger.getlogger(dynamicroutingdatasource.class);
  
  @override
  protected object determinecurrentlookupkey() {
    //從threadlocal中取值
    return dynamicdatasourcecontextholder.get();
  }
}

第1種方式雖然實現比較加單,劣勢就是不同數據源的mapper文件不能在同一包名,就顯得不太靈活了。所以為了更加靈活的作為一個組件的存在,作者采用的第二種方式實現。

設計思路

springboot 動態數據源的實現方法(Mybatis+Druid)

  1. 當請求經過被注解修飾的類后,此時會進入到切面邏輯中。
  2. 切面邏輯會獲取注解中設置的key值,然后將該值存入到threadlocal中
  3. 執行完切面邏輯后,會執行abstractroutingdatasource.determinecurrentlookupkey()方法,然后從threadlocal中獲取之前設置的key值,然后將該值返回。
  4. 由于abstractroutingdatasource的targetdatasources是一個map,保存了數據源key和數據源的對應關系,所以能夠順利的找到該對應的數據源。

源碼解讀

org.springframework.jdbc.datasource.lookup.abstractroutingdatasource,如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public abstract class abstractroutingdatasource extends abstractdatasource implements initializingbean {
  
  private map<object, object> targetdatasources;
  private object defaulttargetdatasource;
  private boolean lenientfallback = true;
  private datasourcelookup datasourcelookup = new jndidatasourcelookup();
  private map<object, datasource> resolveddatasources;
  private datasource resolveddefaultdatasource;
  
    protected datasource determinetargetdatasource() {
    assert.notnull(this.resolveddatasources, "datasource router not initialized");
    object lookupkey = determinecurrentlookupkey();
    datasource datasource = this.resolveddatasources.get(lookupkey);
    if (datasource == null && (this.lenientfallback || lookupkey == null)) {
      datasource = this.resolveddefaultdatasource;
    }
    if (datasource == null) {
      throw new illegalstateexception("cannot determine target datasource for lookup key [" + lookupkey + "]");
    }
    return datasource;
  }
 
  /**
   * determine the current lookup key. this will typically be
   * implemented to check a thread-bound transaction context.
   * <p>allows for arbitrary keys. the returned key needs
   * to match the stored lookup key type, as resolved by the
   * {@link #resolvespecifiedlookupkey} method.
   */
  protected abstract object determinecurrentlookupkey();
  
  //........略

targetdatasources是一個map結構,保存了key與數據源的對應關系;

datasourcelookup是一個datasourcelookup類型,默認實現是jndidatasourcelookup。點開該類源碼會發現,它實現了通過key獲取datasource的邏輯。當然,這里可以通過setdatasourcelookup()來改變其屬性,因為關于此處有一個坑,后面會講到。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class jndidatasourcelookup extends jndilocatorsupport implements datasourcelookup {
 
  public jndidatasourcelookup() {
    setresourceref(true);
  }
 
  @override
  public datasource getdatasource(string datasourcename) throws datasourcelookupfailureexception {
    try {
      return lookup(datasourcename, datasource.class);
    }
    catch (namingexception ex) {
      throw new datasourcelookupfailureexception(
          "failed to look up jndi datasource with name '" + datasourcename + "'", ex);
    }
  }
 
}

組件使用

多數據源

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# db1
spring.datasource.master.url = jdbc:mysql://127.0.0.1:3306/test?useunicode=true&characterencoding=utf8&usessl=false
spring.datasource.master.username = root
spring.datasource.master.password = 123456
spring.datasource.master.driverclassname = com.mysql.jdbc.driver
spring.datasource.master.validationquery = true
spring.datasource.master.testonborrow = true
## db2
spring.datasource.slave.url = jdbc:mysql://127.0.0.1:3306/test1?useunicode=true&characterencoding=utf8&usessl=false
spring.datasource.slave.username = root
spring.datasource.slave.password = 123456
spring.datasource.slave.driverclassname = com.mysql.jdbc.driver
spring.datasource.slave.validationquery = true
spring.datasource.slave.testonborrow = true
 
#主數據源名稱
spring.maindb=master
#mapperper包路徑
mapper.basepackages =com.btps.xli.multidb.demo.mapper

單數據源

為了讓使用者能夠用最小的改動實現最好的效果,作者對單數據源的多種配置做了兼容。

示例配置1(配置數據源名稱):

?
1
2
3
4
5
6
7
8
9
10
11
spring.datasource.master.url = jdbc:mysql://127.0.0.1:3306/test?useunicode=true&characterencoding=utf8&usessl=false
spring.datasource.master.username = root
spring.datasource.master.password = 123456
spring.datasource.master.driverclassname = com.mysql.jdbc.driver
spring.datasource.master.validationquery = true
spring.datasource.master.testonborrow = true
 
# mapper包路徑
mapper.basepackages = com.goofly.xli.multidb.demo.mapper
# 主數據源名稱
spring.maindb=master

示例配置2(不配置數據源名稱):

?
1
2
3
4
5
6
7
8
9
spring.datasource.url = jdbc:mysql://127.0.0.1:3306/test?useunicode=true&characterencoding=utf8&usessl=false
spring.datasource.username = root
spring.datasource.password = 123456
spring.datasource.driverclassname = com.mysql.jdbc.driver
spring.datasource.validationquery = true
spring.datasource.testonborrow = true
 
# mapper包路徑
mapper.basepackages = com.goofly.xli.multidb.demo.mapper

踩坑之路

多數據源的循環依賴

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
description:
 
the dependencies of some of the beans in the application context form a cycle:
 
  happinesscontroller (field private com.db.service.happinessservice com.db.controller.happinesscontroller.happinessservice)
   
  happinessserviceimpl (field private com.db.mapper.masterdao com.db.service.happinessserviceimpl.masterdao)
   
  masterdao defined in file [e:\gitrepository\framework-gray\test-db\target\classes\com\db\mapper\masterdao.class]
   
  sqlsessionfactory defined in class path resource [com/goofly/xli/datasource/core/dynamicdatasourceconfiguration.class]
┌─────┐
| dynamicdatasource defined in class path resource [com/goofly/xli/datasource/core/dynamicdatasourceconfiguration.class]
↑   ↓
| firstdatasource defined in class path resource [com/goofly/xli/datasource/core/dynamicdatasourceconfiguration.class]
↑   ↓
| datasourceinitializer

解決方案:

在spring boot啟動的時候排除datasourceautoconfiguration即可。如下:

?
1
2
3
4
5
6
@springbootapplication(exclude = {datasourceautoconfiguration.class})
public class dbmain {
  public static void main(string[] args) {
    springapplication.run(dbmain.class, args);
  }
}

但是作者在創建多數據源的時候由于并未創建多個datasource的bean,而是只創建了一個即需要做動態數據源的那個bean。 其他的datasource則直接創建實例然后存放在map里面,然后再設置到dynamicroutingdatasource#settargetdatasources即可。

因此這種方式也不會出現循環依賴的問題!

動態刷新數據源

筆者在設計之初是想構建一個動態刷新數據源的方案,所以利用了springcloud的@refreshscope去標注數據源,然后利用refreshscope#refresh實現刷新。但是在實驗的時候發現由druid創建的數據源會因此而關閉,由spring的datasourcebuilder創建的數據源則不會發生任何變化。
? 最后關于此也沒能找到解決方案。同時思考,如果只能的可以實現動態刷新的話,那么數據源的原有連接會因為刷新而中斷嗎還是會有其他處理?

多數據源事務

有這么一種特殊情況,一個事務中調用了兩個不同數據源,這個時候動態切換數據源會因此而失效。

翻閱了很多文章,大概找了2中解決方案,一種是atomikos進行事務管理,但是貌似性能并不是很理想。

另外一種則是通過優先級控制,切面的的優先級必須要大于數據源的優先級,用注解@order控制。

此處留一個坑!

git代碼地址 

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持服務器之家。

原文鏈接:https://segmentfault.com/a/1190000017989247

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 午夜爱情动作片P | 午夜小视频免费观看 | 国产精品igao视频网网址 | 国产精品视频久久久 | 日韩一区二区在线视频 | 亚洲精品资源 | 亚洲精品国产综合久久一线 | 狠狠色狠狠色综合婷婷tag | videosxxxx老女人 | 91在线精品国产丝袜超清 | 久久国产伦子伦精品 | 91小视频在线观看免费版高清 | 国外欧美一区另类中文字幕 | 日韩激情视频在线观看 | 美女脱一光二净的视频 | 欧美高清日韩 | 很黄的网站在线观看 | 免费视频一级片 | 国产在线影院 | 99视频在线看观免费 | 999任你躁在线精品免费不卡 | 和直男装修工在工地啪 | 香蕉在线精品一区二区 | 欧美va在线 | 色老板在线播放 | 香蕉人人超人人超碰超国产 | 日韩欧免费一区二区三区 | 9420高清视频在线观看网百度 | 草女人逼 | 日本伊人久久 | 教室眠催白丝美女校花 | 91短视频版高清在线观看免费 | 亚洲精品tv久久久久久久久久 | 高跟翘臀老师后进式视频 | 无遮挡免费h肉动漫在线观看 | 女班长的放荡日记高h | 好大好爽好涨太深了小喜 | 国产91素人搭讪系列天堂 | 久久久久久久久性潮 | babes性欧美30 | 97就去干 |