SpringBoot利用dynamic-datasource-spring-boot-starter解決多數(shù)據(jù)源問(wèn)題
概要
自己閑暇時(shí)間想實(shí)現(xiàn)一個(gè)多租戶平臺(tái),需要實(shí)現(xiàn)數(shù)據(jù)分離,動(dòng)態(tài)配置生成數(shù)據(jù)源,憑著自己的感覺(jué)搭建了一套簡(jiǎn)單的方案
dynamic-datasource-spring-boot-starter 是一個(gè)用于在 Spring Boot 項(xiàng)目中實(shí)現(xiàn)動(dòng)態(tài)數(shù)據(jù)源切換的工具,這里主要通過(guò)這個(gè)工具實(shí)現(xiàn)在系統(tǒng)運(yùn)行中創(chuàng)建數(shù)據(jù)庫(kù),生成數(shù)據(jù)庫(kù)連接。
整體架構(gòu)構(gòu)想
【創(chuàng)建數(shù)據(jù)源】使用DefaultDataSourceCreator類下的createDataSource方法創(chuàng)建數(shù)據(jù)源。
【存儲(chǔ)數(shù)據(jù)源】使用DynamicRoutingDataSource方法的addDataSource方法以鍵值對(duì)存儲(chǔ)數(shù)據(jù)源。
【切換數(shù)據(jù)源】使用**@DS**注解動(dòng)態(tài)切換數(shù)據(jù)源。
操作步驟
創(chuàng)建數(shù)據(jù)源
創(chuàng)建數(shù)據(jù)源方法大致如下,主要就是創(chuàng)建數(shù)據(jù)庫(kù)的參數(shù)配置,讓生成器生成數(shù)據(jù)源。
//默認(rèn)使用Mysql8.0,并且連接mysql數(shù)據(jù)庫(kù)表,通過(guò)這個(gè)連接創(chuàng)建新的數(shù)據(jù)庫(kù)數(shù)據(jù)
DynamicRoutingDataSource ds = (DynamicRoutingDataSource) dataSource;
// 數(shù)據(jù)源參數(shù)配置
DataSourceProperty dataSourceProperty = new DataSourceProperty();
//固定使用mysql8
String dbClassType = "com.mysql.cj.jdbc.Driver";
String dbUrl = String.format("jdbc:mysql://%s:%s/%s?characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2B8", dbIp, dbPort, dbName);
//這里使用IP進(jìn)行區(qū)分
dataSourceProperty.setPoolName(dbIp);
dataSourceProperty.setUsername(userName);
dataSourceProperty.setPassword(passWord);
dataSourceProperty.setUrl(dbUrl);
dataSourceProperty.setDriverClassName(dbClassType);
// 數(shù)據(jù)源全部懶加載,避免一次性聲明過(guò)多連接
dataSourceProperty.setLazy(true);
// 生成數(shù)據(jù)源
DataSource dataSource = dataSourceCreator.createDataSource(dataSourceProperty);
//最后存儲(chǔ)到ds中,后面可以用個(gè)不同的key去連接不同的數(shù)據(jù)源
ds.addDataSource(dbIp,dataSource);
后面在調(diào)用不同數(shù)據(jù)庫(kù)的時(shí)候只需要使用**@DS**注解,代碼如下:
/**
* 調(diào)用不同的數(shù)據(jù)連接生成新的數(shù)據(jù)庫(kù)
* @param onlySignIp
* @param dbName
* @return
*/
@DS(value = "#onlySignIp")
public int createDataSource(String onlySignIp,@Param("dbName")String dbName);切換數(shù)據(jù)源
一開(kāi)始我覺(jué)得這樣就可以直接在系統(tǒng)中自定義創(chuàng)建數(shù)據(jù)庫(kù),dynamic-datasource底層在初始化加載的時(shí)候會(huì)生成一條責(zé)任鏈,一共為三個(gè)節(jié)點(diǎn)分別為DsHeaderProcessor、DsSessionProcessor、DsSpelExpressionProcessor分別對(duì)應(yīng)#header、#session和spel表達(dá)式,源碼如下:
@Bean
@ConditionalOnMissingBean
public DsProcessor dsProcessor(BeanFactory beanFactory) {
DsHeaderProcessor headerProcessor = new DsHeaderProcessor();
DsSessionProcessor sessionProcessor = new DsSessionProcessor();
DsSpelExpressionProcessor spelExpressionProcessor = new DsSpelExpressionProcessor();
spelExpressionProcessor.setBeanResolver(new BeanFactoryResolver(beanFactory));
headerProcessor.setNextProcessor(sessionProcessor);
sessionProcessor.setNextProcessor(spelExpressionProcessor);
return headerProcessor;
}在這里 根據(jù)底層代碼理論上來(lái)說(shuō)是不會(huì)應(yīng)該有問(wèn)題的,好像是用它自定義的責(zé)任鏈不行,這里我就自己重寫了這條責(zé)任鏈(也就自己復(fù)制源代碼將他注入到spring容器中),然后在重寫DsSpelExpressionProcessor,通過(guò)spel表達(dá)式來(lái)選擇數(shù)據(jù)源。
public class DsSpelExpressionProcessor extends DsProcessor {
/**
* 參數(shù)發(fā)現(xiàn)器
*/
private static final ParameterNameDiscoverer NAME_DISCOVERER = new DefaultParameterNameDiscoverer();
/**
* Express語(yǔ)法解析器
*/
private static final ExpressionParser PARSER = new SpelExpressionParser();
/**
* 解析上下文的模板
* 對(duì)于默認(rèn)不設(shè)置的情況下,從參數(shù)中取值的方式 #param1
* 設(shè)置指定模板 ParserContext.TEMPLATE_EXPRESSION 后的取值方式: #{#param1}
* issues: https://github.com/baomidou/dynamic-datasource-spring-boot-starter/issues/199
*/
private ParserContext parserContext = new ParserContext() {
@Override
public boolean isTemplate() {
return false;
}
@Override
public String getExpressionPrefix() {
return null;
}
@Override
public String getExpressionSuffix() {
return null;
}
};
private BeanResolver beanResolver;
@Override
public boolean matches(String key) {
return true;
}
@Override
public String doDetermineDatasource(MethodInvocation invocation, String key) {
Object[] arguments = invocation.getArguments();
StandardEvaluationContext context = new StandardEvaluationContext(arguments);
//默認(rèn)使用第一個(gè)參數(shù)
String replace = key.replace("#", "");
context.setVariable(replace,arguments[0]);
final Object value = PARSER.parseExpression(key, parserContext).getValue(context);
return value == null ? null : value.toString();
}
public void setParserContext(ParserContext parserContext) {
this.parserContext = parserContext;
}
public void setBeanResolver(BeanResolver beanResolver) {
this.beanResolver = beanResolver;
}
}這樣配合@DS注解就可以實(shí)現(xiàn)數(shù)據(jù)源的動(dòng)態(tài)切換了。
后續(xù)問(wèn)題
在需要切換數(shù)據(jù)源的情況下,將對(duì)應(yīng)數(shù)據(jù)源Key信息作為Mapper第一個(gè)參數(shù)傳遞
例如:
/**
* 調(diào)用不同的數(shù)據(jù)連接生成新的數(shù)據(jù)庫(kù)
* @param onlySignIp
* @param dbName
* @return
*/
@DS(value = "#onlySignIp")
public int createDataSource(String onlySignIp,@Param("dbName")String dbName);在切換數(shù)據(jù)源的情況下必須要加上**@DS**注解,而且還必須將對(duì)應(yīng)數(shù)據(jù)源的Key作為第一個(gè)參數(shù)進(jìn)行傳入
小結(jié)
通過(guò)對(duì) dynamic-datasource-spring-boot-starter 的剖析,我們簡(jiǎn)單的實(shí)現(xiàn)了自定義創(chuàng)建數(shù)據(jù)源,切換數(shù)據(jù)源的操作
到此這篇關(guān)于SpringBoot利用dynamic-datasource-spring-boot-starter解決多數(shù)據(jù)源問(wèn)題的文章就介紹到這了,更多相關(guān)SpringBoot多數(shù)據(jù)源內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- springboot項(xiàng)目如何配置多數(shù)據(jù)源
- SpringBoot實(shí)現(xiàn)JPA多數(shù)據(jù)源配置小結(jié)
- SpringBoot實(shí)現(xiàn)多數(shù)據(jù)源配置的示例詳解
- SpringBoot中實(shí)現(xiàn)多數(shù)據(jù)源連接和切換的方案
- 淺析SpringBoot多數(shù)據(jù)源實(shí)現(xiàn)方案
- springboot項(xiàng)目實(shí)現(xiàn)多數(shù)據(jù)源配置使用dynamic-datasource-spring-boot-starter的操作步驟
- springboot配置多數(shù)據(jù)源的一款框架(dynamic-datasource-spring-boot-starter)
相關(guān)文章
Idea工具中使用Mapper對(duì)象有紅線的解決方法
mapper對(duì)象在service層有紅線,項(xiàng)目可以正常使用,想知道為什么會(huì)出現(xiàn)這種情,接下來(lái)通過(guò)本文給大家介紹下Idea工具中使用Mapper對(duì)象有紅線的問(wèn)題,需要的朋友可以參考下2022-09-09
MyBatis整合Redis實(shí)現(xiàn)二級(jí)緩存的示例代碼
這篇文章主要介紹了MyBatis整合Redis實(shí)現(xiàn)二級(jí)緩存的示例代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-08-08
IntelliJ IDEA 小技巧之Bookmark(書簽)的使用
這篇文章主要介紹了IntelliJ IDEA 小技巧之Bookmark(書簽)的使用,本文通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-07-07
java使用google身份驗(yàn)證器實(shí)現(xiàn)動(dòng)態(tài)口令驗(yàn)證的示例
本篇文章主要介紹了java使用google身份驗(yàn)證器實(shí)現(xiàn)動(dòng)態(tài)口令驗(yàn)證的示例,具有一定的參考價(jià)值,有興趣的可以了解一下2017-08-08
SpringMVC + servlet3.0 文件上傳的配置和實(shí)現(xiàn)代碼
本篇文章主要介紹了SpringMVC + servlet3.0 文件上傳的配置和實(shí)現(xiàn)代碼,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下。2017-04-04
解決cmd運(yùn)行java程序“找不到文件”提示的方案
在本篇文章里小編給大家分享的是關(guān)于解決cmd運(yùn)行java程序“找不到文件”提示的方案,有需要的朋友們可以參考下。2020-02-02
Java 使用getClass().getResourceAsStream()方法獲取資源
這篇文章主要介紹了Java 使用getClass().getResourceAsStream()方法獲取資源的相關(guān)資料,這里主要講解哪種方式可以獲取到文件資源,需要的朋友可以參考下2017-07-07
SpringBoot集成Druid監(jiān)控頁(yè)面最小化配置操作
這篇文章主要介紹了SpringBoot集成Druid監(jiān)控頁(yè)面最小化配置操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-09-09

