最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

SpringBoot多數(shù)據(jù)源配置的全過程記錄

 更新時間:2021年11月05日 11:02:04   作者:裝在瓶子里的西班牙陽光  
在用SpringBoot開發(fā)項目時,隨著業(yè)務量的擴大,我們通常會進行數(shù)據(jù)庫拆分或是引入其他數(shù)據(jù)庫,從而我們需要配置多個數(shù)據(jù)源,下面這篇文章主要給大家介紹了關于SpringBoot多數(shù)據(jù)源配置的相關資料,需要的朋友可以參考下

前言

多數(shù)據(jù)源的核心就是向 IOC 容器注入 AbstractRoutingDataSource 和如何切換數(shù)據(jù)源。注入的方式可以是注冊 BeanDefinition 或者是構建好的 Bean,切換數(shù)據(jù)源的方式可以是方法參數(shù)或者是注解切換(其他的沒想象出來),具體由需求決定。

我的需求是統(tǒng)計多個庫的數(shù)據(jù),將結果寫入另一個數(shù)據(jù)庫,統(tǒng)計的數(shù)據(jù)庫數(shù)量是不定的,無法通過 @Bean 直接注入,又是統(tǒng)計任務,DAO 層注解切換無法滿足,因此選擇注冊(AbstractRoutingDataSource 的)BeanDefinition 和方法參數(shù)切換來實現(xiàn)。下面以統(tǒng)計統(tǒng)計中日韓用戶到結果庫為例。

配置文件

master 為結果庫,其他為被統(tǒng)計的數(shù)據(jù)庫(china、japan 可以用枚舉唯一標識,當然也可以用 String):

dynamic:
  dataSources:
    master:
      driver-class-name: com.mysql.cj.jdbc.Driver
      url: jdbc:mysql://localhost:3306/result?useUnicode=true&characterEncoding=utf8xxxxxxxx
      username: root
      password: 123456
    china:
      driver-class-name: com.mysql.cj.jdbc.Driver
      url: jdbc:mysql://localhost:3306/china?useUnicode=true&characterEncoding=utf8xxxxxxxx
      username: root
      password: 123456
    japan:
      driver-class-name: com.mysql.cj.jdbc.Driver
      url: jdbc:mysql://localhost:3306/japan?useUnicode=true&characterEncoding=utf8xxxxxxxx
      username: root
      password: 123456
    korea:
      driver-class-name: com.mysql.cj.jdbc.Driver
      url: jdbc:mysql://localhost:3306/korea?useUnicode=true&characterEncoding=utf8xxxxxxxx
      username: root
      password: 123456

對應的配置類:

package com.statistics.dynamicds.core.config;

import com.statistics.dynamicds.core.Country;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;

import java.util.Map;

import static com.statistics.dynamicds.core.config.MultiDataSourceProperties.PREFIX;

@Data
@Configuration
@ConfigurationProperties(prefix = PREFIX)
public class MultiDataSourceProperties {
  public static final String PREFIX = "dynamic";
  private Map<Country, DataSourceProperties> dataSources;

  @Data
  public static class DataSourceProperties {
    private String driverClassName;
    private String url;
    private String username;
    private String password;
  }
}
package com.statistics.dynamicds.core;

public enum Country {
  MASTER("master", 0),

  CHINA("china", 86),
  JAPAN("japan", 81),
  KOREA("korea", 82),
  // 其他國家省略

  private final String name;
  private final int id;

  Country(String name, int id) {
    this.name = name;
    this.id = id;
  }

  public int getId() {
    return id;
  }

  public String getName() {
    return name;
  }
}

依賴

ORM 用的 JPA,SpringBoot 版本為 2.3.7.RELEASE,通過 Lombok 簡化 GetSet。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

<dependency>
     <groupId>org.projectlombok</groupId>
     <artifactId>lombok</artifactId>
     <version>1.18.22</version>
     <scope>provided</scope>
</dependency>

構建 AbstractRoutingDataSource

Spring 的動態(tài)數(shù)據(jù)源需要注入 AbstractRoutingDataSource,因為配置文件中被統(tǒng)計數(shù)據(jù)源不是固定的,所以不能通過 @Bean 注解注入,需要手動構建。

要在啟動類加上 @Import(MultiDataSourceImportBeanDefinitionRegistrar.class)。

要在啟動類加上 @Import(MultiDataSourceImportBeanDefinitionRegistrar.class)。

要在啟動類加上 @Import(MultiDataSourceImportBeanDefinitionRegistrar.class),重要的事情寫三行。

package com.statistics.dynamicds.autoconfig;

import com.statistics.dynamicds.core.DynamicDataSourceRouter;
import com.statistics.dynamicds.core.Country;
import com.statistics.dynamicds.core.config.MultiDataSourceProperties;
import com.zaxxer.hikari.HikariDataSource;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotationMetadata;

import javax.annotation.Nonnull;
import java.util.Map;
import java.util.stream.Collectors;

import static com.statistics.dynamicds.core.config.MultiDataSourceProperties.PREFIX;

public class MultiDataSourceImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar, EnvironmentAware {
  public static final String DATASOURCE_BEANNAME = "dynamicDataSourceRouter";
  private Environment environment;

  @Override
  public void registerBeanDefinitions(@Nonnull AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
    MultiDataSourceProperties multiDataSourceProperties = Binder.get(environment)
            .bind(PREFIX, MultiDataSourceProperties.class)
            .orElseThrow(() -> new RuntimeException("no found dynamicds config"));
    final HikariDataSource[] defaultTargetDataSource = {null};
    Map<Country, HikariDataSource> targetDataSources = multiDataSourceProperties.getDataSources().entrySet().stream()
            .collect(Collectors.toMap(
                    Map.Entry::getKey,
                    entry -> {
              MultiDataSourceProperties.DataSourceProperties dataSourceProperties = entry.getValue();
              HikariDataSource dataSource = DataSourceBuilder.create()
                      .type(HikariDataSource.class)
                      .driverClassName(dataSourceProperties.getDriverClassName())
                      .url(dataSourceProperties.getUrl())
                      .username(dataSourceProperties.getUsername())
                      .password(dataSourceProperties.getPassword())
                      .build();
              dataSource.setPoolName("HikariPool-" + entry.getKey());
              if (Country.MASTER == entry.getKey()) {
                defaultTargetDataSource[0] = dataSource;
              }
              return dataSource;
            }));
    AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(DynamicDataSourceRouter.class)
            .addConstructorArgValue(defaultTargetDataSource[0])
            .addConstructorArgValue(targetDataSources)
            .getBeanDefinition();
    registry.registerBeanDefinition(DATASOURCE_BEANNAME, beanDefinition);
  }

  @Override
  public void setEnvironment(@Nonnull Environment environment) {
    this.environment = environment;
  }
}

上面代碼中 MultiDataSourceProperties 不是由 @Resource 或者 @Autowired 獲取的是因為 ImportBeanDefinitionRegistrar 執(zhí)行的很早,此時 @ConfigurationProperties 的配置參數(shù)類還沒有注入,因此要手動獲取(加 @ConfigurationProperties 注解是為了使 IOC 容器中其他 Bean 能獲取配置的 Country,以此來切換數(shù)據(jù)源)。

下面是 AbstractRoutingDataSource 的實現(xiàn)類 DynamicDataSourceRouter:

package com.statistics.dynamicds.core;

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

import java.util.Map;

public class DynamicDataSourceRouter extends AbstractRoutingDataSource {
  public DynamicDataSourceRouter(Object defaultTargetDataSource, Map<Object, Object> targetDataSources) {
    this.setDefaultTargetDataSource(defaultTargetDataSource);
    this.setTargetDataSources(targetDataSources);
  }

  @Override
  protected Object determineCurrentLookupKey() {
    return DataSourceContextHolder.getLookupKey();
  }
}

數(shù)據(jù)源切換

數(shù)據(jù)源的切換由 DataSourceContextHolder 和切面 DynamicDataSourceAspect 控制:

package com.statistics.dynamicds.core;

public class DataSourceContextHolder {
  private static final ThreadLocal<Country> HOLDER = ThreadLocal.withInitial(() -> Country.MASTER);

  public static void setLookupKey(Country lookUpKey) {
    HOLDER.set(lookUpKey);
  }

  public static Country getLookupKey() {
    return HOLDER.get();
  }

  public static void clear() {
    HOLDER.remove();
  }
}
package com.statistics.dynamicds.core;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class DynamicDataSourceAspect {

  @Pointcut("execution(* com.statistics.dao..*.*(..))")
  void aspect() {

  }

  @Around("aspect()")
  public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
    for (Object arg : joinPoint.getArgs()) {
      if (arg instanceof Country) {
        DataSourceContextHolder.setLookupKey((Country) arg);
        break;
      }
    }
    try {
      return joinPoint.proceed();
    }finally {
      DataSourceContextHolder.clear();
    }
  }
}

目錄

.
└─com
    └─statistics
        │  StatisticsApplication.java
        │
        ├─dao
        │      UserDao.java
        │
        ├─dynamicds
        │  ├─autoconfig
        │  │      MultiDataSourceImportBeanDefinitionRegistrar.java
        │  │
        │  └─core
        │      │  DataSourceContextHolder.java
        │      │  DynamicDataSourceAspect.java
        │      │  DynamicDataSourceRouter.java
        │      │  Province.java
        │      │
        │      └─config
        │              MultiDataSourceProperties.java

總結

以上就完成了多數(shù)據(jù)源配置,使用時只需要按照在 dao 層的方法參數(shù)中加一個 Country 枚舉就可以了。

如果無法用枚舉標識數(shù)據(jù)源也可以換成 String,關于這個數(shù)據(jù)源的其他信息在內部類 DataSourceProperties 加一個 map 即可,總之就是按照自己的需求擴展。

相關文章

  • 淺談基于Token的WEB后臺認證機制

    淺談基于Token的WEB后臺認證機制

    這篇文章主要介紹了淺談基于Token的WEB后臺認證機制,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-12-12
  • SpringData JPA實體映射與關系映射的實現(xiàn)

    SpringData JPA實體映射與關系映射的實現(xiàn)

    Spring Data JPA作為Spring生態(tài)系統(tǒng)中的核心項目,通過JPA規(guī)范提供了優(yōu)雅而強大的實體映射與關系處理機制,下面就來介紹一下,感興趣的可以了解一下
    2025-04-04
  • JavaFX實現(xiàn)簡易時鐘效果(一)

    JavaFX實現(xiàn)簡易時鐘效果(一)

    這篇文章主要為大家詳細介紹了JavaFX實現(xiàn)簡易時鐘效果的第一篇,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2020-11-11
  • Spring MVC溫故而知新系列教程之請求映射RequestMapping注解

    Spring MVC溫故而知新系列教程之請求映射RequestMapping注解

    這篇文章主要介紹了Spring MVC溫故而知新系列教程之請求映射RequestMapping注解的相關知識,文中給大家介紹了RequestMapping注解提供的幾個屬性及注解說明,感興趣的朋友跟隨腳本之家小編一起學習吧
    2018-05-05
  • Java BeanDefination接口詳細講解

    Java BeanDefination接口詳細講解

    BeanDefinition是spring里面bean的一個建模對象,就相當于class對象是普通java對象的建模對象一樣。可能在spring作用的各種業(yè)務場景中,class對象并不能完成spring對bean的抽象,所以弄了一個BeanDefinition作為bean的抽象建模對象
    2022-11-11
  • Fluent Mybatis如何做到代碼邏輯和sql邏輯的合一

    Fluent Mybatis如何做到代碼邏輯和sql邏輯的合一

    對比原生Mybatis, Mybatis Plus或者其他框架,F(xiàn)luentMybatis提供了哪些便利呢?很多朋友對這一問題不是很清楚,今天小編給大家?guī)硪黄坛剃P于Fluent Mybatis如何做到代碼邏輯和sql邏輯的合一,一起看看吧
    2021-08-08
  • Java中Volatile關鍵字詳解及代碼示例

    Java中Volatile關鍵字詳解及代碼示例

    這篇文章主要介紹了Java中Volatile關鍵字詳解及代碼示例,分為兩個部分,第一部分介紹了Volatile關鍵字的基本概念等內容,第二部分分享了實例代碼,具有一定參考價值,需要的朋友可以了解下。
    2017-11-11
  • SpringBoot測試junit遇到的坑及解決

    SpringBoot測試junit遇到的坑及解決

    這篇文章主要介紹了SpringBoot測試junit遇到的坑及解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-01-01
  • 快速入門Java中的Lambda表達式

    快速入門Java中的Lambda表達式

    Lambda作為函數(shù)式編程中的基礎部分,在其他編程語言中早就廣為使用,但在Java領域中發(fā)展較慢,直到java8,才開始支持Lambda。網(wǎng)上關于Lambda的教程很多,今天小編給大家分享一篇快速入手Lambda的教程。
    2016-08-08
  • Spring Boot 2.0快速構建服務組件全步驟

    Spring Boot 2.0快速構建服務組件全步驟

    這篇文章主要給大家介紹了關于Spring Boot 2.0快速構建服務組件的相關資料,文中通過示例代碼介紹的非常詳細,對大家學習或者使用Spring Boot 2.0具有一定的參考學習價值,需要的朋友們下面來一起學習學習吧
    2019-04-04

最新評論

金坛市| 梨树县| 印江| 文昌市| 民权县| 高碑店市| 蓬溪县| 鄂温| 云阳县| 泰顺县| 龙泉市| 德庆县| 奉贤区| 株洲市| 屏山县| 远安县| 虹口区| 大庆市| 饶阳县| 会泽县| 中西区| 珠海市| 淮南市| 屏山县| 富蕴县| 莲花县| 广丰县| 湘乡市| 琼中| 刚察县| 伊川县| 尖扎县| 临邑县| 即墨市| 隆尧县| 高州市| 浑源县| 土默特左旗| 晋城| 九龙县| 海晏县|