Spring boot 從數(shù)據(jù)庫讀取配置信息動態(tài)切換多數(shù)據(jù)源

現(xiàn)在項目中配置多數(shù)據(jù)源的情況很多氧敢,但是大多數(shù)情況都是在yml中配置询张,或者用配置中心例如spring config或者nacos中的配置文件中寫死的。這樣做的壞處有兩點:

  1. 如果十個數(shù)據(jù)源,那么配置文件就太繁瑣了
  2. 無法在項目不重啟的情況下添加數(shù)據(jù)源(比如某個表原本在A數(shù)據(jù)源弯屈,但是現(xiàn)在因為表中數(shù)據(jù)太大恋拷,想要在B數(shù)據(jù)源也建個表來查詢,這里不要杠分庫分表)

所以還有一種很流行的寫法蔬顾,就是把所有的數(shù)據(jù)庫連接信息放在一張表里。然后在項目中讀取這張表的數(shù)據(jù)酪我,動態(tài)創(chuàng)建數(shù)據(jù)庫連接且叁。當(dāng)然了這種寫法肯定比事先寫死要繁瑣一點秩伞。但是其優(yōu)點也很明顯。下面我們詳細(xì)說一下如何實現(xiàn)這種做法纱新。

思路整理

做一件事之前一個清晰的思路很重要,下面我們先理解我們要做什么:

  1. 首先項目中要有一個默認(rèn)的數(shù)據(jù)庫遇汞。這個數(shù)據(jù)庫里要有一張數(shù)據(jù)庫信息表簿废。包括不限于 url,username,password,driverClassName,數(shù)據(jù)庫名稱等(因為我們數(shù)據(jù)庫會涉及到不同的數(shù)據(jù)庫,所以增加了驅(qū)動字段族檬。后面我會把我自己建的表結(jié)構(gòu)貼出來)单料。
  2. 大體思路應(yīng)該是每次執(zhí)行數(shù)據(jù)庫操作的時候選擇要使用的DataSource,所以每個數(shù)據(jù)源要有一個唯一標(biāo)識來指定扫尖。
  3. 我們要把所有的數(shù)據(jù)源保存到一個map中,key是唯一標(biāo)識甩恼,value是DataSource,這樣在請求的時候選擇唯一標(biāo)識以后可以直接查找到對應(yīng)的DataSource來使用媳拴。
  4. 如果指定的標(biāo)識在map中不存在,則應(yīng)該走創(chuàng)建步驟塞关。甚至這里都可以用懶加載的模式子巾,用到哪個數(shù)據(jù)源再去創(chuàng)建,不需要初始化线梗。
  5. 為了多線程之間不沖突,所以我們應(yīng)該用ThreadLocal保存當(dāng)前指定的DataSource瘾婿。

主要的幾個步驟就是這樣烤咧,其中還有一些細(xì)節(jié)就在代碼里細(xì)說。

實現(xiàn)

數(shù)據(jù)庫連接表:

CREATE TABLE `t_db_config` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `url` varchar(255) DEFAULT NULL,
  `username` varchar(255) DEFAULT NULL,
  `password` varchar(255) DEFAULT NULL,
  `driver_class_name` varchar(255) DEFAULT NULL,
  `create_person` varchar(255) DEFAULT NULL,
  `create_time` datetime DEFAULT NULL,
  `update_person` varchar(255) DEFAULT NULL,
  `update_time` datetime DEFAULT NULL,
  `del_status` int(1) DEFAULT NULL,
  `db_name` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;

這個表的實體類dao層service層的生成我就不說了煮嫌,感興趣的可以看我另一個mybatis plus代碼生成器的文章:
MyBatisPlus中代碼生成器的簡單使用 - 簡書 (jianshu.com)

創(chuàng)建數(shù)據(jù)源集合:這個就是自己建個類繼承AbstractRoutingDataSource方法就行。代碼如下:

import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.stat.DruidDataSourceStatManager;
import com.lenovo.entity.DbConfigEntity;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import org.springframework.util.StringUtils;
import java.sql.Connection;
import java.sql.DriverManager;
import java.util.Map;
import java.util.Set;


public class DynamicDataSource extends AbstractRoutingDataSource {
    private boolean debug = true;
    private final Logger log = LoggerFactory.getLogger(getClass());
    private Map<Object, Object> dynamicTargetDataSources;
    private Object dynamicDefaultTargetDataSource;

    @Override
    protected Object determineCurrentLookupKey() {
        String datasource = DBContextHolder.getDataSource();
        if (!StringUtils.isEmpty(datasource)) {
            Map<Object, Object> dynamicTargetDataSources2 = this.dynamicTargetDataSources;
            if (dynamicTargetDataSources2.containsKey(datasource)) {
                log.info("---當(dāng)前數(shù)據(jù)源:" + datasource + "---");
            } else {
                log.info("不存在的數(shù)據(jù)源:");
                return null;
            }
        } else {
            log.info("---當(dāng)前數(shù)據(jù)源:默認(rèn)數(shù)據(jù)源---");
        }
        return datasource;
    }

    /**
     * 檢查當(dāng)前數(shù)據(jù)源是否存在,如果存在測試是否可用懦冰。不存在或者不可用創(chuàng)建新的數(shù)據(jù)源
     * @param dbConfig
     * @throws Exception
     */
    public void createDataSourceWithCheck(DbConfigEntity dbConfig) throws Exception {
        String dbName = dbConfig.getDbName();
        log.info("正在檢查數(shù)據(jù)源:"+dbName);
        Map<Object, Object> dynamicTargetDataSources2 = this.dynamicTargetDataSources;
        if (dynamicTargetDataSources2.containsKey(dbName)) {
            log.info("數(shù)據(jù)源"+dbName+"之前已經(jīng)創(chuàng)建,準(zhǔn)備測試數(shù)據(jù)源是否正常...");
            DruidDataSource druidDataSource = (DruidDataSource) dynamicTargetDataSources2.get(dbName);
            boolean rightFlag = true;
            Connection connection = null;
            try {
                log.info(dbName+"數(shù)據(jù)源的概況->當(dāng)前閑置連接數(shù):"+druidDataSource.getPoolingCount());
                long activeCount = druidDataSource.getActiveCount();
                log.info(dbName+"數(shù)據(jù)源的概況->當(dāng)前活動連接數(shù):"+activeCount);
                if(activeCount > 0) {
                    log.info(dbName+"數(shù)據(jù)源的概況->活躍連接堆棧信息:"+druidDataSource.getActiveConnectionStackTrace());
                }
                log.info("準(zhǔn)備獲取數(shù)據(jù)庫連接...");
                connection = druidDataSource.getConnection();
                log.info("數(shù)據(jù)源"+dbName+"正常");
            } catch (Exception e) {
                log.error(e.getMessage(),e); //把異常信息打印到日志文件
                rightFlag = false;
                log.info("緩存數(shù)據(jù)源"+dbName+"已失效框往,準(zhǔn)備刪除...");
                if(delDatasources(dbName)) {
                    log.info("緩存數(shù)據(jù)源刪除成功");
                } else {
                    log.info("緩存數(shù)據(jù)源刪除失敗");
                }
            } finally {
                if(null != connection) {
                    connection.close();
                }
            }
            if(rightFlag) {
                log.info("不需要重新創(chuàng)建數(shù)據(jù)源");
                return;
            } else {
                log.info("準(zhǔn)備重新創(chuàng)建數(shù)據(jù)源...");
                createDataSource(dbConfig);
                log.info("重新創(chuàng)建數(shù)據(jù)源完成");
            }
        } else {
            createDataSource(dbConfig);
        }
    }


    /**
     * 真正的創(chuàng)建數(shù)據(jù)源的方法
     * @param dbConfig
     * @return
     */
    public boolean createDataSource(DbConfigEntity dbConfig) {
        try {
            try { // 排除連接不上的錯誤
                Class.forName(dbConfig.getDriverClassName());
                DriverManager.getConnection(dbConfig.getUrl(), dbConfig.getUsername(), dbConfig.getPassword());// 相當(dāng)于連接數(shù)據(jù)庫
            } catch (Exception e) {
                log.info("數(shù)據(jù)庫鏈接錯誤,url:{},username:{},password:{},錯誤原因:{}",
                        dbConfig.getUrl(), dbConfig.getUsername(), dbConfig.getPassword(),e.getMessage());
                return false;
            }
            @SuppressWarnings("resource")
            DruidDataSource druidDataSource = new DruidDataSource();
            druidDataSource.setName(dbConfig.getDbName());
            druidDataSource.setDriverClassName(dbConfig.getDriverClassName());
            druidDataSource.setUrl(dbConfig.getUrl());
            druidDataSource.setUsername(dbConfig.getUsername());
            druidDataSource.setPassword(dbConfig.getPassword());
            druidDataSource.setInitialSize(1); //初始化時建立物理連接的個數(shù)椰弊。初始化發(fā)生在顯示調(diào)用init方法瓤鼻,或者第一次getConnection時
            druidDataSource.setMaxActive(20); //最大連接池數(shù)量
            druidDataSource.setMaxWait(60000); //獲取連接時最大等待時間,單位毫秒茬祷。當(dāng)鏈接數(shù)已經(jīng)達(dá)到了最大鏈接數(shù)的時候,應(yīng)用如果還要獲取鏈接就會出現(xiàn)等待的現(xiàn)象秸妥,等待鏈接釋放并回到鏈接池,如果等待的時間過長就應(yīng)該踢掉這個等待粥惧,不然應(yīng)用很可能出現(xiàn)雪崩現(xiàn)象
            druidDataSource.setMinIdle(5); //最小連接池數(shù)量
            String validationQuery = "select 1";
            druidDataSource.setValidationQuery(validationQuery); //用來檢測連接是否有效的sql,要求是一個查詢語句起惕。如果validationQuery為null咏删,testOnBorrow、testOnReturn督函、testWhileIdle都不會起作用。
            druidDataSource.setTestOnBorrow(true); //申請連接時執(zhí)行validationQuery檢測連接是否有效草穆,這里建議配置為TRUE搓译,防止取到的連接不可用
            druidDataSource.setTestWhileIdle(true);//建議配置為true,不影響性能些己,并且保證安全性嘿般。申請連接的時候檢測,如果空閑時間大于timeBetweenEvictionRunsMillis炉奴,執(zhí)行validationQuery檢測連接是否有效。
            druidDataSource.setFilters("stat");//屬性類型是字符串赛糟,通過別名的方式配置擴(kuò)展插件砸逊,常用的插件有:監(jiān)控統(tǒng)計用的filter:stat日志用的filter:log4j防御sql注入的filter:wall
            druidDataSource.setTimeBetweenEvictionRunsMillis(60000); //配置間隔多久才進(jìn)行一次檢測,檢測需要關(guān)閉的空閑連接师逸,單位是毫秒
            druidDataSource.setMinEvictableIdleTimeMillis(180000); //配置一個連接在池中最小生存的時間,單位是毫秒动知,這里配置為3分鐘180000
            druidDataSource.setKeepAlive(true); //打開druid.keepAlive之后,當(dāng)連接池空閑時盒粮,池中的minIdle數(shù)量以內(nèi)的連接,空閑時間超過minEvictableIdleTimeMillis脂男,則會執(zhí)行keepAlive操作种呐,即執(zhí)行druid.validationQuery指定的查詢SQL宰翅,一般為select * from dual,只要minEvictableIdleTimeMillis設(shè)置的小于防火墻切斷連接時間汁讼,就可以保證當(dāng)連接空閑時自動做崩眨活檢測,不會被防火墻切斷
            druidDataSource.setRemoveAbandoned(true); //是否移除泄露的連接/超過時間限制是否回收啸箫。
            druidDataSource.setRemoveAbandonedTimeout(3600); //泄露連接的定義時間(要超過最大事務(wù)的處理時間);單位為秒蝉娜。這里配置為1小時
            druidDataSource.setLogAbandoned(true);
            druidDataSource.init();
            this.dynamicTargetDataSources.put(dbConfig.getDbName(), druidDataSource);
            setTargetDataSources(this.dynamicTargetDataSources);// 將map賦值給父類的TargetDataSources
            super.afterPropertiesSet();// 將TargetDataSources中的連接信息放入resolvedDataSources管理
            log.info(dbConfig.getDbName()+"數(shù)據(jù)源初始化成功");
            return true;
        } catch (Exception e) {
            log.error(e + "");
            return false;
        }

    }

    /**
     * 刪除數(shù)據(jù)源
     */
    public boolean delDatasources(String dbName) {
        Map<Object, Object> dynamicTargetDataSources2 = this.dynamicTargetDataSources;
        if (dynamicTargetDataSources2.containsKey(dbName)) {
            Set<DruidDataSource> druidDataSourceInstances = DruidDataSourceStatManager.getDruidDataSourceInstances();
            for (DruidDataSource l : druidDataSourceInstances) {
                if (dbName.equals(l.getName())) {
                    dynamicTargetDataSources2.remove(dbName);
                    DruidDataSourceStatManager.removeDataSource(l);
                    setTargetDataSources(dynamicTargetDataSources2);// 將map賦值給父類的TargetDataSources
                    super.afterPropertiesSet();// 將TargetDataSources中的連接信息放入resolvedDataSources管理
                    return true;
                }
            }
            return false;
        } else {
            return false;
        }
    }

    /**
     * 測試數(shù)據(jù)源連接是否有效
     * @param dbConfig
     * @return
     */
    public boolean testDatasource(DbConfigEntity dbConfig) {
        try {
            Class.forName(dbConfig.getDriverClassName());
            DriverManager.getConnection(dbConfig.getUrl(), dbConfig.getUsername(), dbConfig.getPassword());
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    @Override
    public void setDefaultTargetDataSource(Object defaultTargetDataSource) {
        super.setDefaultTargetDataSource(defaultTargetDataSource);
        this.dynamicDefaultTargetDataSource = defaultTargetDataSource;
    }

    @Override
    public void setTargetDataSources(Map<Object, Object> targetDataSources) {
        super.setTargetDataSources(targetDataSources);
        this.dynamicTargetDataSources = targetDataSources;
    }

    public Map<Object, Object> getDynamicTargetDataSources() {
        return dynamicTargetDataSources;
    }

    public void setDynamicTargetDataSources(Map<Object, Object> dynamicTargetDataSources) {
        this.dynamicTargetDataSources = dynamicTargetDataSources;
    }

    public Object getDynamicDefaultTargetDataSource() {
        return dynamicDefaultTargetDataSource;
    }

    public void setDynamicDefaultTargetDataSource(Object dynamicDefaultTargetDataSource) {
        this.dynamicDefaultTargetDataSource = dynamicDefaultTargetDataSource;
    }

}

上面有很多精細(xì)的判斷召川,反正是我抄的胸遇,主要方法有以下幾個:

  • 檢查是否存在,不存在則調(diào)用創(chuàng)建的方法
  • 創(chuàng)建數(shù)據(jù)源的方法
  • 檢查是否可用
  • 刪除數(shù)據(jù)源

這里有一個細(xì)節(jié):檢驗語句是select 1 之前炒的是select * from dual.但是后來我發(fā)現(xiàn)pg數(shù)據(jù)庫會報錯纸镊。別的就沒啥了,可以針對性配置收捣。

創(chuàng)建線程安全的切換工具類:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class DBContextHolder {
    private final static Logger log = LoggerFactory.getLogger(DBContextHolder.class);
    // 對當(dāng)前線程的操作-線程安全的
    private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>();

    // 調(diào)用此方法庵楷,切換數(shù)據(jù)源
    public static void setDataSource(String dataSource) {
        contextHolder.set(dataSource);
        log.info("已切換到數(shù)據(jù)源:{}",dataSource);
    }

    // 獲取數(shù)據(jù)源
    public static String getDataSource() {
        return contextHolder.get();
    }

    // 刪除數(shù)據(jù)源
    public static void clearDataSource() {
        contextHolder.remove();
        log.info("已切換到主數(shù)據(jù)源");
    }
}

創(chuàng)建默認(rèn)數(shù)據(jù)源Mybatis Plus(這里我把駝峰設(shè)置寫到配置文件中了楣颠,所以還要帶個配置文件):

import com.alibaba.druid.pool.DruidDataSource;
import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
import org.apache.ibatis.session.SqlSessionFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import javax.sql.DataSource;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;

/**
 * @Author : JCccc
 * @CreateTime : 2019/10/22
 * @Description :
 **/
@Configuration
@EnableTransactionManagement
public class DruidDBConfig {
    private final Logger log = LoggerFactory.getLogger(getClass());

    // adi數(shù)據(jù)庫連接信息
    @Value("${spring.datasource.url}")
    private String dbUrl;
    @Value("${spring.datasource.username}")
    private String username;
    @Value("${spring.datasource.password}")
    private String password;
    @Value("${spring.datasource.driver-class-name}")
    private String driverClassName;


    @Bean // 聲明其為Bean實例
    @Primary // 在同樣的DataSource中童漩,首先使用被標(biāo)注的DataSource
    @Qualifier("mainDataSource")
    public DataSource dataSource() throws SQLException {
        DruidDataSource datasource = new DruidDataSource();
        // 基礎(chǔ)連接信息
        datasource.setUrl(this.dbUrl);
        datasource.setUsername(username);
        datasource.setPassword(password);
        datasource.setDriverClassName(driverClassName);
        // 連接池連接信息
        datasource.setInitialSize(5);
        datasource.setMinIdle(5);
        datasource.setMaxActive(20);
        datasource.setMaxWait(60000);
        datasource.setPoolPreparedStatements(true); //是否緩存preparedStatement春锋,也就是PSCache。PSCache對支持游標(biāo)的數(shù)據(jù)庫性能提升巨大期奔,比如說oracle。在mysql下建議關(guān)閉呐萌。
        datasource.setMaxPoolPreparedStatementPerConnectionSize(20);
        datasource.setTestOnBorrow(true); //申請連接時執(zhí)行validationQuery檢測連接是否有效,這里建議配置為TRUE罗晕,防止取到的連接不可用
        datasource.setTestWhileIdle(true);//建議配置為true赠堵,不影響性能,并且保證安全性茫叭。申請連接的時候檢測,如果空閑時間大于timeBetweenEvictionRunsMillis揍愁,執(zhí)行validationQuery檢測連接是否有效。
        String validationQuery = "select 1";
        datasource.setValidationQuery(validationQuery); //用來檢測連接是否有效的sql,要求是一個查詢語句凹髓。如果validationQuery為null,testOnBorrow饵沧、testOnReturn赌躺、testWhileIdle都不會起作用。
        datasource.setFilters("stat,wall");//屬性類型是字符串礼患,通過別名的方式配置擴(kuò)展插件掠归,常用的插件有:監(jiān)控統(tǒng)計用的filter:stat日志用的filter:log4j防御sql注入的filter:wall
        datasource.setTimeBetweenEvictionRunsMillis(60000); //配置間隔多久才進(jìn)行一次檢測悄泥,檢測需要關(guān)閉的空閑連接,單位是毫秒
        datasource.setMinEvictableIdleTimeMillis(180000); //配置一個連接在池中最小生存的時間弹囚,單位是毫秒,這里配置為3分鐘180000
        datasource.setKeepAlive(true); //打開druid.keepAlive之后蛮穿,當(dāng)連接池空閑時毁渗,池中的minIdle數(shù)量以內(nèi)的連接,空閑時間超過minEvictableIdleTimeMillis祝蝠,則會執(zhí)行keepAlive操作,即執(zhí)行druid.validationQuery指定的查詢SQL绎狭,一般為select * from dual,只要minEvictableIdleTimeMillis設(shè)置的小于防火墻切斷連接時間喇聊,就可以保證當(dāng)連接空閑時自動做北目瘢活檢測誓篱,不會被防火墻切斷
        datasource.setRemoveAbandoned(true); //是否移除泄露的連接/超過時間限制是否回收凯楔。
        datasource.setRemoveAbandonedTimeout(3600); //泄露連接的定義時間(要超過最大事務(wù)的處理時間);單位為秒邻遏。這里配置為1小時
        datasource.setLogAbandoned(true);
        return datasource;
    }

    @Bean(name = "dynamicDataSource")
    @Qualifier("dynamicDataSource")
    public DynamicDataSource dynamicDataSource() throws SQLException {
        DynamicDataSource dynamicDataSource = new DynamicDataSource();
        // 默認(rèn)數(shù)據(jù)源配置 DefaultTargetDataSource
        dynamicDataSource.setDefaultTargetDataSource(dataSource());
        Map<Object, Object> targetDataSources = new HashMap<Object, Object>();
        //額外數(shù)據(jù)源配置 TargetDataSources
        targetDataSources.put("mainDataSource", dataSource());
        dynamicDataSource.setTargetDataSources(targetDataSources);
        return dynamicDataSource;
    }

    @Bean
    public SqlSessionFactory sqlSessionFactory() throws Exception {
        MybatisSqlSessionFactoryBean sqlSessionFactoryBean = new MybatisSqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(dynamicDataSource());
        // 設(shè)置mybatis的主配置文件
        ResourcePatternResolver resolver = new   
    PathMatchingResourcePatternResolver();
sqlSessionFactoryBean.setMapperLocations(
        new PathMatchingResourcePatternResolver()
                .getResources("classpath:mapper/*.xml"));
        Resource mybatisConfigXml = resolver.getResource("classpath:mybatis-config.xml");
        sqlSessionFactoryBean.setConfigLocation(mybatisConfigXml);
        return sqlSessionFactoryBean.getObject();
    }
}

注意文中配置文件的路徑是根據(jù)配置來的虐骑。我的mybatis-config.xml位置就是這樣:


mybatis配置位置

然后配置文件內(nèi)容如下(其實就是一個駝峰的設(shè)置):

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>

    <settings>
        <setting name="useGeneratedKeys" value="true"/>
        <setting name="useColumnLabel" value="true"/>
        <setting name="mapUnderscoreToCamelCase" value="true"/>
    </settings>
</configuration>

注意上面的配置是對于mybatis plus來說的廷没。mybatis應(yīng)該要改下sqlSessionFactory那塊,因為一開始我按照mybatis配置會出問題所以修改了颠黎。不過mybatis我沒使用過滞项,不確保正確性砰蠢。

    @Bean
    public SqlSessionFactory sqlSessionFactory() throws Exception {
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(dynamicDataSource());
        //解決手動創(chuàng)建數(shù)據(jù)源后字段到bean屬性名駝峰命名轉(zhuǎn)換失效的問題
        sqlSessionFactoryBean.setConfiguration(configuration());

        // 設(shè)置mybatis的主配置文件
        ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();

        return sqlSessionFactoryBean.getObject();
    }

最后我們建立個切換數(shù)據(jù)源的工具類就行了,代碼可以參考下面:

@Service("dbConfigService")
public class DbConfigServiceImpl extends ServiceImpl<DbConfigMapper, DbConfigEntity> implements DbConfigService {

    @Autowired
    private DbConfigMapper dbConfigMapper;

    @Autowired
    private DynamicDataSource dynamicDataSource;

    @Override
    public List<DbConfigEntity> get() {
        LambdaQueryWrapper<DbConfigEntity> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.eq(DbConfigEntity::getDelStatus, 0);
        List<DbConfigEntity> list = dbConfigMapper.selectList(queryWrapper);
        return list;
    }

    @Override
    public boolean changeDb(String dbName) throws Exception {
        //默認(rèn)切換到主數(shù)據(jù)源,進(jìn)行整體資源的查找
        DBContextHolder.clearDataSource();
        List<DbConfigEntity> dataSourcesList = dbConfigMapper.get();

        for (DbConfigEntity dbConfig : dataSourcesList) {
            if (dbConfig.getDbName().equals(dbName)) {
                System.out.println("需要使用的的數(shù)據(jù)源已經(jīng)找到,dbName是:" + dbName);
                //創(chuàng)建數(shù)據(jù)源連接&檢查 若存在則不需重新創(chuàng)建
                dynamicDataSource.createDataSourceWithCheck(dbConfig);
                //切換到該數(shù)據(jù)源
                DBContextHolder.setDataSource(dbName);
                return true;
            }
        }
        return false;
    }
}

最后我們在代碼中測試一下數(shù)據(jù)源切換:

    @GetMapping("/test")
    public void test() throws Exception{
        //i = 1 查默認(rèn)庫  2 查數(shù)據(jù)庫2
        dbConfigService.changeDb("test2");
        System.out.println(dbConfigService.get());
        DBContextHolder.clearDataSource();
        System.out.println(dbConfigService.get());
    }

我這里為了測試特意兩個庫都建了這個表并添加不同的數(shù)據(jù)打印了律杠。事實證明是切換成功了的竞惋。至于這個dbName,計劃是每個接口在請求的時候都必須要傳這個參數(shù)用來指定要查詢的庫拆宛。確實會麻煩一點,但是也靈活很多股耽。
本篇筆記就到這里钳幅,如果稍微幫到你了記得點個喜歡點個關(guān)注物蝙!也祝大家工作順順利利敢艰!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市震嫉,隨后出現(xiàn)的幾起案子牡属,更是在濱河造成了極大的恐慌,老刑警劉巖逮栅,帶你破解...
    沈念sama閱讀 218,682評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡担映,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,277評論 3 395
  • 文/潘曉璐 我一進(jìn)店門官硝,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人氢架,你說我怎么就攤上這事傻咖。” “怎么了卿操?”我有些...
    開封第一講書人閱讀 165,083評論 0 355
  • 文/不壞的土叔 我叫張陵孙援,是天一觀的道長。 經(jīng)常有香客問我拓售,道長,這世上最難降的妖魔是什么崭放? 我笑而不...
    開封第一講書人閱讀 58,763評論 1 295
  • 正文 為了忘掉前任鸽凶,我火速辦了婚禮,結(jié)果婚禮上吱瘩,老公的妹妹穿的比我還像新娘。我一直安慰自己使碾,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,785評論 6 392
  • 文/花漫 我一把揭開白布拘鞋。 她就那樣靜靜地躺著矢门,像睡著了一般盆色。 火紅的嫁衣襯著肌膚如雪祟剔。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,624評論 1 305
  • 那天宣旱,我揣著相機(jī)與錄音叛薯,去河邊找鬼浑吟。 笑死,一個胖子當(dāng)著我的面吹牛省容,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播腥椒,決...
    沈念sama閱讀 40,358評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼轩触,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了脱柱?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,261評論 0 276
  • 序言:老撾萬榮一對情侶失蹤惨好,失蹤者是張志新(化名)和其女友劉穎随闺,沒想到半個月后日川,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體矩乐,經(jīng)...
    沈念sama閱讀 45,722評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年分歇,在試婚紗的時候發(fā)現(xiàn)自己被綠了欧漱。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,030評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡误甚,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出擅威,到底是詐尸還是另有隱情,我是刑警寧澤郊丛,帶...
    沈念sama閱讀 35,737評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站宾袜,受9級特大地震影響驾窟,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜绅络,卻給世界環(huán)境...
    茶點故事閱讀 41,360評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望杉畜。 院中可真熱鬧衷恭,春花似錦此叠、人聲如沸随珠。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,941評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至软瞎,卻和暖如春拉讯,著一層夾襖步出監(jiān)牢的瞬間涤浇,已是汗流浹背遂唧。 一陣腳步聲響...
    開封第一講書人閱讀 33,057評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留纹烹,地道東北人。 一個月前我還...
    沈念sama閱讀 48,237評論 3 371
  • 正文 我出身青樓铺呵,卻偏偏與公主長得像隧熙,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,976評論 2 355

推薦閱讀更多精彩內(nèi)容