欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

springboot 動(dòng)態(tài)數(shù)據(jù)源的實(shí)現(xiàn)方法(Mybatis+Druid)

 更新時(shí)間:2019年01月23日 10:13:02   作者:時(shí)光沉舊了少年  
這篇文章主要介紹了springboot 動(dòng)態(tài)數(shù)據(jù)源的實(shí)現(xiàn)方法(Mybatis+Druid),小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧

Spring多數(shù)據(jù)源實(shí)現(xiàn)的方式大概有2中,一種是新建多個(gè)MapperScan掃描不同包,另外一種則是通過(guò)繼承AbstractRoutingDataSource實(shí)現(xiàn)動(dòng)態(tài)路由。今天作者主要基于后者做的實(shí)現(xiàn),且方式1的實(shí)現(xiàn)比較簡(jiǎn)單這里不做過(guò)多探討。

實(shí)現(xiàn)方式

方式1的實(shí)現(xiàn)(核心代碼):

@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的實(shí)現(xiàn)(核心代碼):

public class DynamicRoutingDataSource extends AbstractRoutingDataSource {
  private static final Logger log = Logger.getLogger(DynamicRoutingDataSource.class);
  
  @Override
  protected Object determineCurrentLookupKey() {
    //從ThreadLocal中取值
    return DynamicDataSourceContextHolder.get();
  }
}

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

設(shè)計(jì)思路

  1. 當(dāng)請(qǐng)求經(jīng)過(guò)被注解修飾的類后,此時(shí)會(huì)進(jìn)入到切面邏輯中。
  2. 切面邏輯會(huì)獲取注解中設(shè)置的key值,然后將該值存入到ThreadLocal中
  3. 執(zhí)行完切面邏輯后,會(huì)執(zhí)行AbstractRoutingDataSource.determineCurrentLookupKey()方法,然后從ThreadLocal中獲取之前設(shè)置的key值,然后將該值返回。
  4. 由于AbstractRoutingDataSource的targetDataSources是一個(gè)map,保存了數(shù)據(jù)源key和數(shù)據(jù)源的對(duì)應(yīng)關(guān)系,所以能夠順利的找到該對(duì)應(yīng)的數(shù)據(jù)源。

源碼解讀

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

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是一個(gè)map結(jié)構(gòu),保存了key與數(shù)據(jù)源的對(duì)應(yīng)關(guān)系;

dataSourceLookup是一個(gè)DataSourceLookup類型,默認(rèn)實(shí)現(xiàn)是JndiDataSourceLookup。點(diǎn)開該類源碼會(huì)發(fā)現(xiàn),它實(shí)現(xiàn)了通過(guò)key獲取DataSource的邏輯。當(dāng)然,這里可以通過(guò)setDataSourceLookup()來(lái)改變其屬性,因?yàn)殛P(guān)于此處有一個(gè)坑,后面會(huì)講到。

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);
    }
  }

}

組件使用

多數(shù)據(jù)源

# 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

#主數(shù)據(jù)源名稱
spring.maindb=master
#mapperper包路徑
mapper.basePackages =com.btps.xli.multidb.demo.mapper

單數(shù)據(jù)源

為了讓使用者能夠用最小的改動(dòng)實(shí)現(xiàn)最好的效果,作者對(duì)單數(shù)據(jù)源的多種配置做了兼容。

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

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
# 主數(shù)據(jù)源名稱
spring.maindb=master

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

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

踩坑之路

多數(shù)據(jù)源的循環(huán)依賴

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啟動(dòng)的時(shí)候排除DataSourceAutoConfiguration即可。如下:

@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
public class DBMain {
  public static void main(String[] args) {
    SpringApplication.run(DBMain.class, args);
  }
}

但是作者在創(chuàng)建多數(shù)據(jù)源的時(shí)候由于并未創(chuàng)建多個(gè)DataSource的Bean,而是只創(chuàng)建了一個(gè)即需要做動(dòng)態(tài)數(shù)據(jù)源的那個(gè)Bean。 其他的DataSource則直接創(chuàng)建實(shí)例然后存放在Map里面,然后再設(shè)置到DynamicRoutingDataSource#setTargetDataSources即可。

因此這種方式也不會(huì)出現(xiàn)循環(huán)依賴的問題!

動(dòng)態(tài)刷新數(shù)據(jù)源

筆者在設(shè)計(jì)之初是想構(gòu)建一個(gè)動(dòng)態(tài)刷新數(shù)據(jù)源的方案,所以利用了SpringCloud的@RefreshScope去標(biāo)注數(shù)據(jù)源,然后利用RefreshScope#refresh實(shí)現(xiàn)刷新。但是在實(shí)驗(yàn)的時(shí)候發(fā)現(xiàn)由Druid創(chuàng)建的數(shù)據(jù)源會(huì)因此而關(guān)閉,由Spring的DataSourceBuilder創(chuàng)建的數(shù)據(jù)源則不會(huì)發(fā)生任何變化。
​ 最后關(guān)于此也沒能找到解決方案。同時(shí)思考,如果只能的可以實(shí)現(xiàn)動(dòng)態(tài)刷新的話,那么數(shù)據(jù)源的原有連接會(huì)因?yàn)樗⑿露袛鄦徇€是會(huì)有其他處理?

多數(shù)據(jù)源事務(wù)

有這么一種特殊情況,一個(gè)事務(wù)中調(diào)用了兩個(gè)不同數(shù)據(jù)源,這個(gè)時(shí)候動(dòng)態(tài)切換數(shù)據(jù)源會(huì)因此而失效。

翻閱了很多文章,大概找了2中解決方案,一種是Atomikos進(jìn)行事務(wù)管理,但是貌似性能并不是很理想。

另外一種則是通過(guò)優(yōu)先級(jí)控制,切面的的優(yōu)先級(jí)必須要大于數(shù)據(jù)源的優(yōu)先級(jí),用注解@Order控制。

此處留一個(gè)坑!

git代碼地址

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • 利用Spring Social輕松搞定微信授權(quán)登錄的方法示例

    利用Spring Social輕松搞定微信授權(quán)登錄的方法示例

    這篇文章主要介紹了利用Spring Social輕松搞定微信授權(quán)登錄的方法示例,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2018-12-12
  • SpringBoot 內(nèi)嵌 camunda的配置方法

    SpringBoot 內(nèi)嵌 camunda的配置方法

    Camunda是一個(gè)基于Java的框架,支持用于工作流和流程自動(dòng)化的BPMN、用于案例管理的CMMN和用于業(yè)務(wù)決策管理的DMN,這篇文章主要介紹了SpringBoot 內(nèi)嵌 camunda,需要的朋友可以參考下
    2024-06-06
  • Java深入淺出講解多線程的概念到使用

    Java深入淺出講解多線程的概念到使用

    哈哈!經(jīng)過(guò)一個(gè)階段的學(xué)習(xí),Java基礎(chǔ)知識(shí)學(xué)習(xí)終于到多線程了!Java多線程以及后面互斥鎖的概念都是Java基礎(chǔ)學(xué)習(xí)的難點(diǎn),所以我做了一個(gè)總結(jié),希望對(duì)大家也有幫助
    2022-05-05
  • 一文徹底弄懂Java中MultipartFile接口和File類

    一文徹底弄懂Java中MultipartFile接口和File類

    MultipartFile是一個(gè)接口,我們可以理解為是Spring?給我們綁定的一個(gè)在使用文件上傳等時(shí)簡(jiǎn)便實(shí)現(xiàn)的口子,這篇文章主要給大家介紹了關(guān)于如何通過(guò)一文徹底弄懂Java中MultipartFile接口和File類的相關(guān)資料,需要的朋友可以參考下
    2023-11-11
  • spring 操作elasticsearch查詢使用方法

    spring 操作elasticsearch查詢使用方法

    本篇文章主要介紹了spring 操作elasticsearch使用方法,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-05-05
  • springboot?整合?SA-Token?使用詳解

    springboot?整合?SA-Token?使用詳解

    本文詳細(xì)介紹了SA-Token這款安全框架的使用,并結(jié)合實(shí)際操作演示了如何集成到springboot項(xiàng)目中,感興趣的朋友跟隨小編一起看看吧
    2024-08-08
  • spring security中的csrf防御原理(跨域請(qǐng)求偽造)

    spring security中的csrf防御原理(跨域請(qǐng)求偽造)

    這篇文章主要介紹了spring security中的csrf防御機(jī)制原理解析(跨域請(qǐng)求偽造),本文通過(guò)實(shí)例代碼詳解的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2019-12-12
  • springboot項(xiàng)目如何使用切面記錄用戶操作日志

    springboot項(xiàng)目如何使用切面記錄用戶操作日志

    這篇文章主要介紹了springboot項(xiàng)目如何使用切面記錄用戶操作日志,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-10-10
  • Spring @Async無(wú)法實(shí)現(xiàn)異步的解決方案

    Spring @Async無(wú)法實(shí)現(xiàn)異步的解決方案

    這篇文章主要介紹了Spring @Async無(wú)法實(shí)現(xiàn)異步的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-10-10
  • Java接口默認(rèn)方法帶來(lái)的問題分析【二義性問題】

    Java接口默認(rèn)方法帶來(lái)的問題分析【二義性問題】

    這篇文章主要介紹了Java接口默認(rèn)方法帶來(lái)的問題,結(jié)合實(shí)例形式分析了java接口帶來(lái)的二義性問題,需要的朋友可以參考下
    2019-08-08

最新評(píng)論