溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

Springboot動(dòng)態(tài)切換數(shù)據(jù)源怎么實(shí)現(xiàn)

發(fā)布時(shí)間:2021-12-16 18:19:12 來(lái)源:億速云 閱讀:413 作者:iii 欄目:開(kāi)發(fā)技術(shù)

這篇文章主要介紹“Springboot動(dòng)態(tài)切換數(shù)據(jù)源怎么實(shí)現(xiàn)”,在日常操作中,相信很多人在Springboot動(dòng)態(tài)切換數(shù)據(jù)源怎么實(shí)現(xiàn)問(wèn)題上存在疑惑,小編查閱了各式資料,整理出簡(jiǎn)單好用的操作方法,希望對(duì)大家解答”Springboot動(dòng)態(tài)切換數(shù)據(jù)源怎么實(shí)現(xiàn)”的疑惑有所幫助!接下來(lái),請(qǐng)跟著小編一起來(lái)學(xué)習(xí)吧!

前言

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

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

具體實(shí)現(xiàn):

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

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

在配置文件中配置多個(gè)數(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ù)庫(kù)連接池信息
spring.datasource.hikari.maximum-pool-size=32
spring.datasource.hikari.minimum-idle=16

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

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

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

public class DataSourceContextHolder {

    /**
     * 通過(guò)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類,并實(shí)現(xiàn)determineCurrentLookupKey方法,該方法是指定當(dāng)前默認(rèn)數(shù)據(jù)源的方法。

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

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

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

@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ù)庫(kù)鎖需要的bean全部交給spring容器中了,使用時(shí)直接通過(guò)DataSourceContextHolder.setDataBaseType(DataSourceEnum dataSourceEnum);這個(gè)方法指定數(shù)據(jù)源對(duì)應(yīng)的枚舉類即可。

原理分析:

Springboot動(dòng)態(tài)切換數(shù)據(jù)源怎么實(shí)現(xiàn)

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

Springboot動(dòng)態(tài)切換數(shù)據(jù)源怎么實(shí)現(xiàn)

這個(gè)類里維護(hù)了兩個(gè)Map來(lái)存儲(chǔ)數(shù)據(jù)庫(kù)連接信息:

@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;

下面對(duì)上面的幾個(gè)屬性進(jìn)行說(shuō)明:

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

Springboot動(dòng)態(tài)切換數(shù)據(jù)源怎么實(shí)現(xiàn)

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

Springboot動(dòng)態(tài)切換數(shù)據(jù)源怎么實(shí)現(xiàn)

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

Springboot動(dòng)態(tài)切換數(shù)據(jù)源怎么實(shí)現(xiàn)

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

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

Springboot動(dòng)態(tài)切換數(shù)據(jù)源怎么實(shí)現(xiàn)

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

Springboot動(dòng)態(tài)切換數(shù)據(jù)源怎么實(shí)現(xiàn)

到此,關(guān)于“Springboot動(dòng)態(tài)切換數(shù)據(jù)源怎么實(shí)現(xiàn)”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實(shí)踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識(shí),請(qǐng)繼續(xù)關(guān)注億速云網(wǎng)站,小編會(huì)繼續(xù)努力為大家?guī)?lái)更多實(shí)用的文章!

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI