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

Springboot動態(tài)切換數(shù)據(jù)源的具體實現(xiàn)與原理分析

 更新時間:2021年12月12日 15:43:01   作者:既然頭發(fā)留不住  
目前有個需求,需要使用不同的數(shù)據(jù)源,例如某業(yè)務(wù)要用A數(shù)據(jù)源,另一個業(yè)務(wù)要用B數(shù)據(jù)源,所以下面這篇文章主要給大家介紹了關(guān)于Springboot動態(tài)切換數(shù)據(jù)源的具體實現(xiàn)與原理分析,需要的朋友可以參考下

前言

在springboot項目中只需一句代碼即可實現(xiàn)多個數(shù)據(jù)源之間的切換:

// 切換sqlserver數(shù)據(jù)源:
DataSourceContextHolder.setDataBaseType(DataSourceEnum.SQLSERVER_DATASOURCE);
......
// 切換mysql數(shù)據(jù)源    
DataSourceContextHolder.setDataBaseType(DataSourceEnum.MYSQL_DATASOURCE);

具體實現(xiàn):

本實例基于springboot2.5+版本實現(xiàn)。

1.配置數(shù)據(jù)源:

在配置文件中配置多個數(shù)據(jù)源的連接信息,用不同的前綴作為區(qū)別:

# sqlserver數(shù)據(jù)源1:前綴為:spring.datasource.sqlserver
spring.datasource.sqlserver.driver-class-name=com.microsoft.sqlserver.jdbc.SQLServerDriver
spring.datasource.sqlserver.jdbc-url=jdbc:sqlserver://localhost:1433;DatabaseName=test
spring.datasource.sqlserver.username=sa
spring.datasource.sqlserver.password=sa
# mysql數(shù)據(jù)源1:前綴為:spring.datasource.mysql
spring.datasource.mysql.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.mysql.jdbc-url=jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8&useSSL=false&allowMultiQueries=true
spring.datasource.mysql.username=root
spring.datasource.mysql.password=root
# sqlLite數(shù)據(jù)源1:前綴為:spring.datasource.sqlite
spring.datasource.sqlite.driver-class-name=org.sqlite.JDBC
spring.datasource.sqlite.jdbc-url=jdbc:sqlite:D://sqllite//test.db
spring.datasource.sqlite.username=
spring.datasource.sqlite.password=
# sqlserver數(shù)據(jù)源2:前綴為:spring.datasource.sqlserver2
spring.datasource.sqlserver2.driver-class-name=com.microsoft.sqlserver.jdbc.SQLServerDriver
spring.datasource.sqlserver2.jdbc-url=jdbc:sqlserver://localhost;DatabaseName=test1
spring.datasource.sqlserver2.username=sa
spring.datasource.sqlserver2.password=sa
# 配置數(shù)據(jù)庫連接池信息
spring.datasource.hikari.maximum-pool-size=32
spring.datasource.hikari.minimum-idle=16

2.新建枚舉類DataSourceEnum,有幾個數(shù)據(jù)源對應(yīng)設(shè)置幾個枚舉類。

public enum DataSourceEnum {
    MYSQL_DATASOURCE,
    SQLSERVER_DATASOURCE,
    SQLSERVER2_DATASOURCE,
    SQLLITE_DATASOURCE
}

3.新建數(shù)據(jù)庫切換工具類DataSourceContextHolder,這里通過ThreadLocal類型的變量來存儲當(dāng)前數(shù)據(jù)源枚舉類,同時能夠保證線程安全。

public class DataSourceContextHolder {

    /**
     * 通過ThreadLocal保證線程安全
     */
    private static final ThreadLocal<DataSourceEnum> contextHolder = new ThreadLocal<>();

    /**
     * 設(shè)置數(shù)據(jù)源變量
     * @param dataSourceEnum 數(shù)據(jù)源變量
     */
    public static void setDataBaseType(DataSourceEnum dataSourceEnum) {
        System.out.println("修改數(shù)據(jù)源為:" + dataSourceEnum);
        contextHolder.set(dataSourceEnum);
    }

    /**
     * 獲取數(shù)據(jù)源變量
     * @return 數(shù)據(jù)源變量
     */
    public static DataSourceEnum getDataBaseType() {
        DataSourceEnum dataSourceEnum = contextHolder.get() == null ? DataSourceEnum.MYSQL_DATASOURCE : contextHolder.get();
        System.out.println("當(dāng)前數(shù)據(jù)源的類型為:" + dataSourceEnum);
        return dataSourceEnum;
    }

    /**
     * 清空數(shù)據(jù)類型
     */
    public static void clearDataBaseType() {
        contextHolder.remove();
    }

4.新建DynamicDataSource類繼承AbstractRoutingDataSource類,并實現(xiàn)determineCurrentLookupKey方法,該方法是指定當(dāng)前默認(rèn)數(shù)據(jù)源的方法。

public class DynamicDataSource extends AbstractRoutingDataSource {
    @Override
    protected Object determineCurrentLookupKey() {
        return DataSourceContextHolder.getDataBaseType();
    }
}

這個類看似內(nèi)容不多,但其實繼承了AbstractRoutingDataSource類是實現(xiàn)動態(tài)切換數(shù)據(jù)源的關(guān)鍵。

5.新建DataSourceConfig類用來創(chuàng)建bean的實例,其中包括各數(shù)據(jù)源的DataSource實例,DynamicDataSource實例以及跟Mybatis相關(guān)的SqlSessionFactory或Spring的JdbcTemplate實例。

@Configuration
public class DataSourceConfig {
    @Bean(name = "sqlserverDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.sqlserver")
    public DataSource getDateSource1() {
        return DataSourceBuilder.create().build();
    }

    @Bean(name = "sqlserver2DataSource")
    @ConfigurationProperties(prefix = "spring.datasource.sqlserver2")
    public DataSource getDateSource11() {
        return DataSourceBuilder.create().build();
    }

    @Bean(name = "mysqlDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.mysql")
    public DataSource getDateSource2() {
        return DataSourceBuilder.create().build();
    }


    @Bean(name = "sqlLiteDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.sqlite")
    public DataSource getDateSource3() {
        return DataSourceBuilder.create().build();
    }

    @Bean(name = "dynamicDataSource")
    public DynamicDataSource DataSource(@Qualifier("sqlserverDataSource") DataSource sqlserverDataSource,
                                        @Qualifier("sqlserver2DataSource") DataSource sqlserver2DataSource,
                                        @Qualifier("mysqlDataSource") DataSource mysqlDataSource,
                                        @Qualifier("sqlLiteDataSource") DataSource sqlLiteDataSource) {
        //配置多數(shù)據(jù)源
        Map<Object, Object> targetDataSource = new HashMap<>();
        targetDataSource.put(DataSourceEnum.SQLSERVER_DATASOURCE, sqlserverDataSource);
        targetDataSource.put(DataSourceEnum.MYSQL_DATASOURCE, mysqlDataSource);
        targetDataSource.put(DataSourceEnum.SQLLITE_DATASOURCE, sqlLiteDataSource);
        targetDataSource.put(DataSourceEnum.SQLSERVER2_DATASOURCE, sqlserver2DataSource);
        DynamicDataSource dataSource = new DynamicDataSource();
        //多數(shù)據(jù)源
        dataSource.setTargetDataSources(targetDataSource);
        //默認(rèn)數(shù)據(jù)源
        dataSource.setDefaultTargetDataSource(sqlserverDataSource);
        return dataSource;
    }
    @Bean(name = "SqlSessionFactory")
    public SqlSessionFactory test1SqlSessionFactory(@Qualifier("dynamicDataSource") DataSource dynamicDataSource)
            throws Exception {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setDataSource(dynamicDataSource);
        return bean.getObject();
    }

    @Bean(name = "JdbcTemplate")
    public JdbcTemplate test1JdbcTemplate(@Qualifier("dynamicDataSource") DataSource dynamicDataSource) {
        return new JdbcTemplate(dynamicDataSource);
    }
}

這樣就把我們切換數(shù)據(jù)庫鎖需要的bean全部交給spring容器中了,使用時直接通過DataSourceContextHolder.setDataBaseType(DataSourceEnum dataSourceEnum);這個方法指定數(shù)據(jù)源對應(yīng)的枚舉類即可。

原理分析:

其實我們新建數(shù)據(jù)庫連接的時候也是通過DataSource來獲取連接的,這里的AbstractRoutingDataSource也是通過了DataSource中的getConnection方法來獲取連接的。

這個類里維護(hù)了兩個Map來存儲數(shù)據(jù)庫連接信息:

@Nullable
private Map<Object, Object> targetDataSources; 

@Nullable
private Object defaultTargetDataSource;

private boolean lenientFallback = true;

private DataSourceLookup dataSourceLookup = new JndiDataSourceLookup();

@Nullable
private Map<Object, DataSource> resolvedDataSources;

@Nullable
private DataSource resolvedDefaultDataSource;

下面對上面的幾個屬性進(jìn)行說明:

其中第一個targetDataSources是一個Map對象,在我們上面第五步創(chuàng)建DynamicDataSource實例的時候?qū)⒍鄠€數(shù)據(jù)源的DataSource類,放入到這個Map中去,這里的Key是枚舉類,values就是DataSource類。

第二個defaultTargetDataSource是默認(rèn)的數(shù)據(jù)源,就是DynamicDataSource中唯一重寫的方法來給這個對象賦值的。

第三個lenientFallback是一個標(biāo)識,是當(dāng)指定數(shù)據(jù)源不存在的時候是否采用默認(rèn)數(shù)據(jù)源,默認(rèn)是true,設(shè)置為false之后如果找不到指定數(shù)據(jù)源將會返回null.

第四個dataSourceLookup是用來解析指定的數(shù)據(jù)源對象為DataSource實例的。默認(rèn)是JndiDataSourceLookup實例,繼承自DataSourceLookup接口。

第五個resolvedDataSources也是一個Map對象,這里是存放指定數(shù)據(jù)源解析后的DataSource對象。

第六個resolvedDefaultDataSource是默認(rèn)的解析后的DataSource數(shù)據(jù)源對象上面的getConnection方法就是從這個變量中拿到DataSource實例并獲取連接的。

總結(jié)

相關(guān)文章

  • Java 獲取當(dāng)前時間及實現(xiàn)時間倒計時功能【推薦】

    Java 獲取當(dāng)前時間及實現(xiàn)時間倒計時功能【推薦】

    這篇文章主要介紹了Java 獲取當(dāng)前時間及實現(xiàn)時間倒計時功能 ,本文通過實例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價值,需要的朋友可以參考下
    2019-05-05
  • SpringBoot自定義錯誤處理邏輯詳解

    SpringBoot自定義錯誤處理邏輯詳解

    這篇文章主要介紹了SpringBoot自定義錯誤處理邏輯,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)吧
    2022-10-10
  • Java結(jié)構(gòu)型設(shè)計模式之適配器模式詳解

    Java結(jié)構(gòu)型設(shè)計模式之適配器模式詳解

    適配器模式,即將某個類的接口轉(zhuǎn)換成客戶端期望的另一個接口的表示,主要目的是實現(xiàn)兼容性,讓原本因為接口不匹配,沒辦法一起工作的兩個類,可以協(xié)同工作。本文將通過示例詳細(xì)介紹適配器模式,需要的可以參考一下
    2022-09-09
  • MyBatis攔截器的原理與使用

    MyBatis攔截器的原理與使用

    本文全面的講解了MyBatis攔截器的作用原理及使用方法,攔截器的使用可以提升開發(fā)效率,學(xué)習(xí)MyBatis的朋友不妨了解下本文
    2021-06-06
  • java字符串遍歷的幾種常用方法總結(jié)

    java字符串遍歷的幾種常用方法總結(jié)

    Java字符串是一系列的Unicode字符序列,但是它卻常常被誤認(rèn)為是char序列,這篇文章主要給大家介紹了關(guān)于java字符串遍歷的幾種常用方法,文中通過實例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2023-04-04
  • SpringMvc配置靜態(tài)資源訪問路徑的實現(xiàn)

    SpringMvc配置靜態(tài)資源訪問路徑的實現(xiàn)

    本文主要介紹了SpringMvc配置靜態(tài)資源訪問路徑的實現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-07-07
  • 詳解基于java的Socket聊天程序——服務(wù)端(附demo)

    詳解基于java的Socket聊天程序——服務(wù)端(附demo)

    這篇文章主要介紹了詳解基于java的Socket聊天程序——服務(wù)端(附demo),具有一定的參考價值,感興趣的小伙伴們可以參考一下。
    2016-12-12
  • C++字符串的處理詳解

    C++字符串的處理詳解

    這篇文章主要介紹了C++ string字符串類,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-08-08
  • Java日常練習(xí)題,每天進(jìn)步一點點(55)

    Java日常練習(xí)題,每天進(jìn)步一點點(55)

    下面小編就為大家?guī)硪黄狫ava基礎(chǔ)的幾道練習(xí)題(分享)。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧,希望可以幫到你
    2021-08-08
  • Spring中利用IOC實現(xiàn)注入的方式

    Spring中利用IOC實現(xiàn)注入的方式

    Spring IOC(控制反轉(zhuǎn))實現(xiàn)依賴注入,將對象創(chuàng)建和依賴關(guān)系的管理交由Spring容器處理,通過注解或XML配置,實現(xiàn)對象之間的松耦合,提高代碼復(fù)用性和可維護(hù)性
    2023-04-04

最新評論