天天看點

執行個體講解Spring boot動态切換資料源

作者:華為雲開發者聯盟

本文分享自華為雲社群《springboot動态切換資料源-雲社群-華為雲》,作者:小陳沒煩惱 。

前言

在公司的系統裡,由于資料量較大,是以配置了多個資料源,它會根據使用者所在的地區去覺得查詢那個一個資料庫。這樣就産生了動态切換資料源的場景。

今天,就模拟一下在主庫查詢訂單資訊查詢不到的時候,切換資料源去曆史庫裡面查詢。

實作效果

首先我們設定查詢的資料庫為db1,可以看到通過訂單号沒有查到訂單資訊,然後我們重置資料源,重新設定為db2,同樣的訂單号就可以查詢到資訊。

執行個體講解Spring boot動态切換資料源

資料庫準備

建立兩個資料庫db1和db2,db1作為主庫,db2作為曆史庫

兩個庫中都有一個訂單表biz_order,主庫中沒有資料,曆史庫中有我們要查詢的資料。

執行個體講解Spring boot動态切換資料源
執行個體講解Spring boot動态切換資料源

代碼編寫

1.建立一個springboot項目,引入所需依賴

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <!--引入druid-替換預設資料庫連接配接池-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.2.15</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.2.2</version>
        </dependency>
        <!--mysql驅動-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.30</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
           

2.application.yaml配置資料庫資訊

這裡我們配置兩個資料庫的資訊

spring:
  datasource:
    db1:
      driver-class-name: com.mysql.cj.jdbc.Driver
      url: jdbc:mysql://localhost/db1?characterEncoding=utf8&characterSetResults=utf8&autoReconnect=true&failOverReadOnly=false
      username: root
      password: root
      type: com.alibaba.druid.pool.DruidDataSource
    db2:
      driver-class-name: com.mysql.cj.jdbc.Driver
      url: jdbc:mysql://localhost/db2?characterEncoding=utf8&characterSetResults=utf8&autoReconnect=true&failOverReadOnly=false
      username: root
      password: root
      type: com.alibaba.druid.pool.DruidDataSource

mybatis:
  mapper-locations: classpath:mapper/*.xml
           

3.建立資料源對象,并注入spring容器中

建立DynamicDataSourceConfig.java檔案,在該配置檔案中讀取yaml配置的資料源資訊,并且通過該資訊構造資料源對象,然後通過@Bean注解注入到spring容器中。

package com.it1997.config;

import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;

import javax.sql.DataSource;

@Configuration
public class DynamicDataSourceConfig {
    @Bean("dataSource1")
    @ConfigurationProperties(prefix = "spring.datasource.db1")
    public DataSource oneDruidDataSource() {
        return DruidDataSourceBuilder.create().build();
    }
    @Bean("dataSource2")
    @ConfigurationProperties(prefix = "spring.datasource.db2")
    public DataSource twoDruidDataSource() {
        return DruidDataSourceBuilder.create().build();
    }

    @Bean
    public DataSourceTransactionManager dataSourceTransactionManager1(@Qualifier("dataSource1") DataSource dataSource1) {
        DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();
        dataSourceTransactionManager.setDataSource(dataSource1);
        return dataSourceTransactionManager;
    }
    @Bean
    public DataSourceTransactionManager dataSourceTransactionManager2(@Qualifier("dataSource2") DataSource dataSource2) {
        DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();
        dataSourceTransactionManager.setDataSource(dataSource2);
        return dataSourceTransactionManager;
    }

}
           

4.資料源配置上下文資訊

建立DynamicDataSourceHolder.java檔案,該檔案通過ThreadLocal,實作為每一個線程建立一個儲存資料源配置的上下文。并且提供setDataSource和getDataSource靜态方法來設定和擷取資料源的名稱。

package com.it1997.config;

public class DynamicDataSourceHolder {
    private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();

    public static void setDataSource(String dataSource) {
        contextHolder.set(dataSource);
    }

    public static String getDataSource() {
        return contextHolder.get();
    }

    public static void clearDataSource() {
        contextHolder.remove();
    }
}
           

5.重寫資料源配置類

建立DynamicDataSource.java檔案,該類繼承AbstractRoutingDataSource 類,重寫父類determineCurrentLookupKey和afterPropertiesSet方法。

這裡我們重寫父類中afterPropertiesSet方法(為什麼要重寫在這個方法,可以看文章最後對于druid的源碼的講解),在這個方法裡我們将spring容器中的所有的資料源,都給放到map裡,然後後續我們根據map中的key來擷取不同的資料源,super.afterPropertiesSet();通過這個方法設定上資料源

在類上加上@Primary注解,讓spring容器優先使用我們自定義的資料源,否則還是會使用預設的資料源配置。

package com.it1997.config;

import org.springframework.context.annotation.Primary;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;

@Component
@Primary
public class DynamicDataSource extends AbstractRoutingDataSource {
    @Resource
    DataSource dataSource1;
    @Resource
    DataSource dataSource2;
    @Override
    protected Object determineCurrentLookupKey() {
        return DynamicDataSourceHolder.getDataSource();
    }

    @Override
    public void afterPropertiesSet() {
        // 初始化所有資料源
        Map<Object, Object> targetDataSource = new HashMap<>();
        targetDataSource.put("db1", dataSource1);
        targetDataSource.put("db2", dataSource2);
        super.setTargetDataSources(targetDataSource);
        super.setDefaultTargetDataSource(dataSource1);
        super.afterPropertiesSet();
    }
}
           

druid資料源配置解讀

點開我們剛剛繼承的AbstractRoutingDataSource抽象類,可以看到它又繼承了AbstractDataSource 實作了InitializingBean接口。

執行個體講解Spring boot動态切換資料源

然後我們在看一下druid的資料源配置是怎麼實作的,點開DruidDataSourceWrapper類,可以看到它也是繼承了AbstractDataSource 實作了InitializingBean接口。并且,讀取的是yaml檔案中spring.datasource.druid下面配置的資料庫連接配接資訊。

而我們自定的一的資料源讀取的是spring.datasource.db1下面配置的資料庫連接配接資訊。

執行個體講解Spring boot動态切換資料源

druid的資料源配置,實作了接口中afterPropertiesSet,在這個方法中設定了資料庫的基本資訊,例如,資料庫連接配接位址、使用者名、密碼以及資料庫連接配接驅動資訊。

關注#華為雲開發者聯盟# 點選下方,第一時間了解華為雲新鮮技術~

華為雲部落格_大資料部落格_AI部落格_雲計算部落格_開發者中心-華為雲