現(xiàn)在項目中配置多數(shù)據(jù)源的情況很多氧敢,但是大多數(shù)情況都是在yml中配置询张,或者用配置中心例如spring config或者nacos中的配置文件中寫死的。這樣做的壞處有兩點:
- 如果十個數(shù)據(jù)源,那么配置文件就太繁瑣了
- 無法在項目不重啟的情況下添加數(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)這種做法纱新。
思路整理
做一件事之前一個清晰的思路很重要,下面我們先理解我們要做什么:
- 首先項目中要有一個默認(rèn)的數(shù)據(jù)庫遇汞。這個數(shù)據(jù)庫里要有一張數(shù)據(jù)庫信息表簿废。包括不限于 url,username,password,driverClassName,數(shù)據(jù)庫名稱等(因為我們數(shù)據(jù)庫會涉及到不同的數(shù)據(jù)庫,所以增加了驅(qū)動字段族檬。后面我會把我自己建的表結(jié)構(gòu)貼出來)单料。
- 大體思路應(yīng)該是每次執(zhí)行數(shù)據(jù)庫操作的時候選擇要使用的DataSource,所以每個數(shù)據(jù)源要有一個唯一標(biāo)識來指定扫尖。
- 我們要把所有的數(shù)據(jù)源保存到一個map中,key是唯一標(biāo)識甩恼,value是DataSource,這樣在請求的時候選擇唯一標(biāo)識以后可以直接查找到對應(yīng)的DataSource來使用媳拴。
- 如果指定的標(biāo)識在map中不存在,則應(yīng)該走創(chuàng)建步驟塞关。甚至這里都可以用懶加載的模式子巾,用到哪個數(shù)據(jù)源再去創(chuàng)建,不需要初始化线梗。
- 為了多線程之間不沖突,所以我們應(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位置就是這樣:
然后配置文件內(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)注物蝙!也祝大家工作順順利利敢艰!