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

SpringBoot實(shí)現(xiàn)多數(shù)據(jù)源的切換實(shí)踐

 更新時(shí)間:2022年03月03日 16:39:15   作者:渣男小四  
這篇主要介紹了SpringBoot實(shí)現(xiàn)多數(shù)據(jù)源的切換,本文基于AOP來實(shí)現(xiàn)數(shù)據(jù)源的切換,文中通過示例代碼介紹的非常詳細(xì),感興趣的小伙伴們可以參考一下

前言

我們在進(jìn)行軟件開發(fā)的過程中,剛開始的時(shí)候因?yàn)闊o法估量系統(tǒng)后期的訪問量和并發(fā)量,所以一開始會采用單體架構(gòu),后期如果網(wǎng)站流量變大, 并發(fā)量變大,那么就可能會將架構(gòu)擴(kuò)展為微服務(wù)架構(gòu),各個(gè)微服務(wù)對應(yīng)一個(gè)數(shù)據(jù)庫,不過這樣的成本就有點(diǎn)大了,可能只是有些模塊用的人比較多, 有些模塊沒什么人用,如果都進(jìn)行服務(wù)拆分,其實(shí)也沒那個(gè)必要,如果有些模塊用的人比較多,那么我們可以采用讀寫分離來減輕壓力,這樣的話, 可以在一定程度上提升系統(tǒng)的用戶體驗(yàn),不過這只是在數(shù)據(jù)庫的I/O上面做方案,如果系統(tǒng)的壓力很大,那么肯定要做負(fù)載均衡,我們今天就先說 實(shí)現(xiàn)數(shù)據(jù)庫的讀寫分離。我們要在代碼層面實(shí)現(xiàn)數(shù)據(jù)庫的讀寫分離,那么核心就是數(shù)據(jù)源的切換,本文基于AOP來實(shí)現(xiàn)數(shù)據(jù)源的切換。

工程結(jié)構(gòu)

└─com
    └─steak
        └─transaction
            │  TransactionDemoApplication.java
            │  
            ├─datasource
            │  │  DatasourceChooser.java
            │  │  DatasourceConfiguration.java
            │  │  DatasourceContext.java
            │  │  DatasourceScope.java
            │  │  DynamicDatasourceAspect.java
            │  │  
            │  └─properties
            │          DynamicDatasourceProperties.java
            │          MainDatasourceProperties.java
            │          
            ├─execute
            │      PlaceOrderExecute.java
            │      
            ├─rest
            │      PlaceOrderApi.java
            │      
            ├─result
            │      R.java
            │      
            └─service
                    IntegralService.java
                    OrderService.java
                    StockService.java

在下面的實(shí)現(xiàn)中,我們一個(gè)有三個(gè)數(shù)據(jù)源,其中有一個(gè)是默認(rèn)的,如果不指定具體的數(shù)據(jù)源,那么就使用默認(rèn)的,我們是基于申明式的方式來切換 數(shù)據(jù)源,只需要在具體的接口上加上注解便能實(shí)現(xiàn)數(shù)據(jù)源的切換。

編碼實(shí)現(xiàn)

yml文件

主數(shù)據(jù)源直接使用spring的配置,其他數(shù)據(jù)源采用自定義的方式,這里采用一個(gè)map結(jié)構(gòu)來定義,方便進(jìn)行解析,可以在yml文件里進(jìn)行添加多個(gè),在代碼 邏輯層面不用改動。

spring:
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    druid:
      username: root
      password: 123456
      url: jdbc:mysql://127.0.0.1:3306/db?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=UTC
      driver-class-name: com.mysql.cj.jdbc.Driver

dynamic:
  datasource: {
    slave1: {
      username: 'root',
      password: '123456',
      url: ' url: jdbc:mysql://127.0.0.1:3306/db?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=UTC',
      driver-class-name: 'com.mysql.cj.jdbc.Driver'
    },
    slave2: {
      username: 'root',
      password: '123456',
      url: ' url: jdbc:mysql://127.0.0.1:3306/db?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=UTC',
      driver-class-name: 'com.mysql.cj.jdbc.Driver'
    }
  }

主數(shù)據(jù)源MainDatasourceProperties

對于主數(shù)據(jù)源,我們單獨(dú)拿出來放在一個(gè)類里面,不過其實(shí)也可以放到dynamic里面,只是需要做一定的處理,我們就簡單的放幾個(gè)連接屬性。

/**
 * @author 劉牌
 * @date 2022/3/220:14
 */
@Component
@ConfigurationProperties(prefix = "spring.datasource.druid")
@Data
public class MainDatasourceProperties {
    private String username;
    private String password;
    private String url;
    private String driverClassName;
}

其他數(shù)據(jù)源DynamicDatasourceProperties

其他數(shù)據(jù)源使用一個(gè)Map來接受yml文件中的數(shù)據(jù)源配置

/**
 * @author 劉牌
 * @date 2022/3/213:47
 */
@Component
@ConfigurationProperties(prefix = "dynamic")
@Data
public class DynamicDatasourceProperties {
    private Map<String,Map<String,String>> datasource;
}

數(shù)據(jù)源配置類DatasourceConfiguration

配置類中主要對DataSource進(jìn)行配置,主數(shù)據(jù)源我們按照正常的bean來定義連接屬性,而其他數(shù)據(jù)數(shù)據(jù)源則使用反射的方式來進(jìn)行連接屬性的 配置,因?yàn)橹鲾?shù)據(jù)源一般是不會變動的,但是其他數(shù)據(jù)源可能會發(fā)生變動,可能會添加,這時(shí)候如果通過硬編碼取配置,那么每增加一個(gè)數(shù)據(jù)源,就需要 增加一個(gè)配置,顯然不太行,所以就使用反射來進(jìn)行賦值。

/**
 * @author 劉牌
 * @date 2022/3/111:34
 */
@Configuration
@AllArgsConstructor
public class DatasourceConfiguration {

    final MainDatasourceProperties mainDatasourceProperties;
    final DynamicDatasourceProperties dynamicDatasourceProperties;

    @Bean
    public DataSource datasource(){
        Map<Object,Object> datasourceMap = new HashMap<>();
        DatasourceChooser datasourceChooser = new DatasourceChooser();
        /**
         * main database
         */
        DruidDataSource mainDataSource = new DruidDataSource();
        mainDataSource.setUsername(mainDatasourceProperties.getUsername());
        mainDataSource.setPassword(mainDatasourceProperties.getPassword());
        mainDataSource.setUrl(mainDatasourceProperties.getUrl());
        mainDataSource.setDriverClassName(mainDatasourceProperties.getDriverClassName());
        datasourceMap.put("main",mainDataSource);
        /**
         * other database
         */
        Map<String, Map<String, String>> sourceMap = dynamicDatasourceProperties.getDatasource();
        sourceMap.forEach((datasourceName,datasourceMaps) -> {
            DruidDataSource dataSource = new DruidDataSource();
            datasourceMaps.forEach((K,V) -> {
                String setField = "set" + K.substring(0, 1).toUpperCase() + K.substring(1);
                //轉(zhuǎn)換yml文件中帶有-符號的屬性
                String[] strings = setField.split("");
                StringBuilder newStr = new StringBuilder();
                for (int i = 0; i < strings.length; i++) {
                    if (strings[i].equals("-")) strings[i + 1] = strings[i + 1].toUpperCase();
                    if (!strings[i].equals("-")) newStr.append(strings[i]);
                }
                try {
                    DruidDataSource.class.getMethod(newStr.toString(),String.class).invoke(dataSource,V);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            });
            datasourceMap.put(datasourceName,dataSource);
        });
        //設(shè)置目標(biāo)數(shù)據(jù)源
        datasourceChooser.setTargetDataSources(datasourceMap);
        //設(shè)置默認(rèn)數(shù)據(jù)源
        datasourceChooser.setDefaultTargetDataSource(mainDataSource);
        return datasourceChooser;
    }
}

上面使用數(shù)據(jù)源配置類中使用反射對其他數(shù)據(jù)源進(jìn)行連接屬性的設(shè)置,然后設(shè)置目標(biāo)數(shù)據(jù)源和默認(rèn)數(shù)據(jù)源,里面有一個(gè)DatasourceChooser。

DatasourceChooser

DatasourceChooser繼承自AbstractRoutingDataSource,AbstractRoutingDataSource可以實(shí)現(xiàn)數(shù)據(jù)源的切換,它里面的 determineCurrentLookupKey()方法需要我們返回一個(gè)數(shù)據(jù)源的名稱,它會自動給我們匹配上數(shù)據(jù)源。

/**
 * @author 劉牌
 * @date 2022/3/112:21
 */
public class DatasourceChooser extends AbstractRoutingDataSource {
    @Override
    protected Object determineCurrentLookupKey() {
        return DatasourceContext.getDatasource();
    }
}

如下是AbstractRoutingDataSource的部分源碼,我們可以看出數(shù)據(jù)源是一個(gè)Map結(jié)構(gòu),可以通過數(shù)據(jù)源名稱查找到對應(yīng)的數(shù)據(jù)源。

package org.springframework.jdbc.datasource.lookup;

public abstract class AbstractRoutingDataSource extends AbstractDataSource implements InitializingBean {
    @Nullable
    private Map<Object, DataSource> resolvedDataSources;
    public AbstractRoutingDataSource() {
    }
    protected DataSource determineTargetDataSource() {
        Object lookupKey = this.determineCurrentLookupKey();
        DataSource dataSource = (DataSource)this.resolvedDataSources.get(lookupKey);
    }

    @Nullable
    protected abstract Object determineCurrentLookupKey();
}

DatasourceContext

DatasourceContext內(nèi)部是一個(gè)ThreadLocal,主要是用來存儲每一個(gè)線程的數(shù)據(jù)源名稱和獲取數(shù)據(jù)源名稱,而數(shù)據(jù)源的名稱我們用過AOP切面 來獲取。

/**
 * @author 劉牌
 * @date 2022/3/112:22
 */
public class DatasourceContext {
    private static final ThreadLocal<String> threadLocal = new ThreadLocal<>();
    public static void setDatasource(String key){
        threadLocal.set(key);
    }
    public static String getDatasource(){
        return threadLocal.get();
    }
}

數(shù)據(jù)源注解DatasourceScope

DatasourceScope標(biāo)準(zhǔn)在方法上面,通過scope來指定數(shù)據(jù)源,不指定默認(rèn)為主數(shù)據(jù)源main

/**
 * @author 劉牌
 * @date 2022/3/111:22
 */
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DatasourceScope {
    String scope() default "main";
}

數(shù)據(jù)源切面DynamicDatasourceAspect

我們在訪問每一個(gè)帶有DatasourceScope注解的方法時(shí),都會經(jīng)過數(shù)據(jù)源切面DynamicDatasourceAspect,獲取到注解上面的 scope的值后,通過DatasourceContext設(shè)置數(shù)據(jù)源名稱,便可實(shí)現(xiàn)對數(shù)據(jù)源的切換。

/**
 * @author 劉牌
 * @date 2022/3/111:34
 */
@Aspect
@Component
public class DynamicDatasourceAspect {

    @Pointcut("@annotation(dataSourceScope)")
    public void dynamicPointcut(DatasourceScope dataSourceScope){}

    @Around(value = "dynamicPointcut(dataSourceScope)", argNames = "joinPoint,dataSourceScope")
    public Object dynamicAround(ProceedingJoinPoint joinPoint , DatasourceScope dataSourceScope) throws Throwable {
        String scope = dataSourceScope.scope();
        DatasourceContext.setDatasource(scope);
        return joinPoint.proceed();
    }
}

使用

只需要在具體的方法上面標(biāo)注數(shù)據(jù)源注解@DatasourceScope,并指定scope的值,便可實(shí)現(xiàn)切換,如果不指定,那么就使用主數(shù)據(jù)源。

/**
 * @author 劉牌
 * @date 2022/3/19:49
 */
@Service
@AllArgsConstructor
public class OrderService {
    
    private JdbcTemplate jdbcTemplate;
    
    @DatasourceScope(scope = "slave1")
    public R saveOrder(Integer userId , Integer commodityId){
        String sql = "INSERT INTO `order`(user_id,commodity_id) VALUES("+userId+","+commodityId+")";
        jdbcTemplate.execute(sql);
        return R.builder().code(200).msg("save order success").build();
    }
}

到此這篇關(guān)于SpringBoot實(shí)現(xiàn)多數(shù)據(jù)源的切換實(shí)踐的文章就介紹到這了,更多相關(guān)SpringBoot多數(shù)據(jù)源切換內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 實(shí)例詳解Java8函數(shù)式接口

    實(shí)例詳解Java8函數(shù)式接口

    本文給大家分析了Java8默認(rèn)方法和函數(shù)式接口實(shí)例其它創(chuàng)建方式,需要的朋友跟著學(xué)習(xí)下吧。
    2017-11-11
  • java 中模擬UDP傳輸?shù)陌l(fā)送端和接收端實(shí)例詳解

    java 中模擬UDP傳輸?shù)陌l(fā)送端和接收端實(shí)例詳解

    這篇文章主要介紹了java 中模擬UDP傳輸?shù)陌l(fā)送端和接收端實(shí)例詳解的相關(guān)資料,需要的朋友可以參考下
    2017-03-03
  • SpringBoot 集成Kaptcha實(shí)現(xiàn)驗(yàn)證碼功能實(shí)例詳解

    SpringBoot 集成Kaptcha實(shí)現(xiàn)驗(yàn)證碼功能實(shí)例詳解

    在一個(gè)web應(yīng)用中驗(yàn)證碼是一個(gè)常見的元素。今天給大家介紹一下kaptcha的和springboot一起使用的簡單例子。感興趣的朋友參考下吧
    2017-08-08
  • SpringBoot項(xiàng)目集成Flyway進(jìn)行數(shù)據(jù)庫版本控制的詳細(xì)教程

    SpringBoot項(xiàng)目集成Flyway進(jìn)行數(shù)據(jù)庫版本控制的詳細(xì)教程

    這篇文章主要介紹了SpringBoot項(xiàng)目集成Flyway進(jìn)行數(shù)據(jù)庫版本控制,本文分步驟通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-07-07
  • 淺談java socket的正確關(guān)閉姿勢

    淺談java socket的正確關(guān)閉姿勢

    這篇文章主要介紹了java socket的正確關(guān)閉姿勢,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-06-06
  • Java中的數(shù)組使用詳解及練習(xí)

    Java中的數(shù)組使用詳解及練習(xí)

    數(shù)組是Java程序中最常見的一種數(shù)據(jù)結(jié)構(gòu),它能夠?qū)⑾嗤愋偷臄?shù)據(jù)用一個(gè)標(biāo)識符封裝到一起,構(gòu)成一個(gè)對象序列或基本數(shù)據(jù)類型,這篇文章主要給大家介紹了關(guān)于Java中數(shù)組使用詳解及練習(xí)的相關(guān)資料,需要的朋友可以參考下
    2024-03-03
  • 解決java idea新建子目錄時(shí)命名不是樹形結(jié)構(gòu)的問題

    解決java idea新建子目錄時(shí)命名不是樹形結(jié)構(gòu)的問題

    這篇文章主要介紹了解決java idea新建子目錄時(shí)命名不是樹形結(jié)構(gòu)的問題,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-08-08
  • 詳解Java多態(tài)對象的類型轉(zhuǎn)換與動態(tài)綁定

    詳解Java多態(tài)對象的類型轉(zhuǎn)換與動態(tài)綁定

    這篇文章主要介紹了詳解Java多態(tài)對象的類型轉(zhuǎn)換與動態(tài)綁定,是Java入門學(xué)習(xí)中的基礎(chǔ)知識,需要的朋友可以參考下
    2015-09-09
  • 詳解Java高級特性之反射

    詳解Java高級特性之反射

    這篇文章主要介紹了Java高級特性之反射的相關(guān)知識,文中講解非常細(xì)致,代碼幫助大家更好的理解和學(xué)習(xí),感興趣的朋友可以了解下
    2020-06-06
  • 詳解Java中的mapstruct插件使用

    詳解Java中的mapstruct插件使用

    mapstruct 的插件是專門用來處理 domin 實(shí)體類與 model 類的屬性映射的,我們只需定義 mapper 接口,mapstruct 在編譯的時(shí)候就會自動的幫我們實(shí)現(xiàn)這個(gè)映射接口,避免了麻煩復(fù)雜的映射實(shí)現(xiàn),對Java?mapstruct使用相關(guān)知識感興趣的朋友一起看看吧
    2022-04-04

最新評論

广昌县| 平舆县| 宁阳县| 栖霞市| 庆城县| 寿光市| 大理市| 安平县| 武定县| 托克逊县| 琼结县| 和龙市| 泰兴市| 海南省| 田阳县| 抚顺市| 宾阳县| 苍山县| 阜南县| 潜山县| 聊城市| 舞钢市| 鹿泉市| 通州市| 北宁市| 雅江县| 彭泽县| 台南县| 铁岭市| 三都| 邢台市| 吉木萨尔县| 淮南市| 石城县| 赤峰市| 芷江| 伊宁市| 洛川县| 正镶白旗| 高邮市| 晴隆县|