SpringBoot多數(shù)據(jù)源解決方案:dynamic-datasource-spring-boot-starter
概要
自己閑暇時間想實現(xiàn)一個多租戶平臺,需要實現(xiàn)數(shù)據(jù)分離,動態(tài)配置生成數(shù)據(jù)源,憑著自己的感覺搭建了一套簡單的方案
dynamic-datasource-spring-boot-starter 是一個用于在 Spring Boot 項目中實現(xiàn)動態(tài)數(shù)據(jù)源切換的工具,這里主要通過這個工具實現(xiàn)在系統(tǒng)運行中創(chuàng)建數(shù)據(jù)庫,生成數(shù)據(jù)庫連接。
整體架構構想
- 【創(chuàng)建數(shù)據(jù)源】使用DefaultDataSourceCreator類下的createDataSource方法創(chuàng)建數(shù)據(jù)源。
- 【存儲數(shù)據(jù)源】使用DynamicRoutingDataSource方法的addDataSource方法以鍵值對存儲數(shù)據(jù)源。
- 【切換數(shù)據(jù)源】使用**@DS**注解動態(tài)切換數(shù)據(jù)源。
操作步驟
創(chuàng)建數(shù)據(jù)源
創(chuàng)建數(shù)據(jù)源方法大致如下,主要就是創(chuàng)建數(shù)據(jù)庫的參數(shù)配置,讓生成器生成數(shù)據(jù)源。
//默認使用Mysql8.0,并且連接mysql數(shù)據(jù)庫表,通過這個連接創(chuàng)建新的數(shù)據(jù)庫數(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進行區(qū)分
dataSourceProperty.setPoolName(dbIp);
dataSourceProperty.setUsername(userName);
dataSourceProperty.setPassword(passWord);
dataSourceProperty.setUrl(dbUrl);
dataSourceProperty.setDriverClassName(dbClassType);
// 數(shù)據(jù)源全部懶加載,避免一次性聲明過多連接
dataSourceProperty.setLazy(true);
// 生成數(shù)據(jù)源
DataSource dataSource = dataSourceCreator.createDataSource(dataSourceProperty);
//最后存儲到ds中,后面可以用個不同的key去連接不同的數(shù)據(jù)源
ds.addDataSource(dbIp,dataSource);
后面在調用不同數(shù)據(jù)庫的時候只需要使用**@DS**注解,代碼如下:
/**
* 調用不同的數(shù)據(jù)連接生成新的數(shù)據(jù)庫
* @param onlySignIp
* @param dbName
* @return
*/
@DS(value = "#onlySignIp")
public int createDataSource(String onlySignIp,@Param("dbName")String dbName);
切換數(shù)據(jù)源
一開始我覺得這樣就可以直接在系統(tǒng)中自定義創(chuàng)建數(shù)據(jù)庫,dynamic-datasource底層在初始化加載的時候會生成一條責任鏈,一共為三個節(jié)點分別為DsHeaderProcessor、DsSessionProcessor、DsSpelExpressionProcessor分別對應#header、#session和spel表達式,源碼如下:
@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ù)底層代碼理論上來說是不會應該有問題的,好像是用它自定義的責任鏈不行,這里我就自己重寫了這條責任鏈(也就自己復制源代碼將他注入到spring容器中),然后在重寫DsSpelExpressionProcessor,通過spel表達式來選擇數(shù)據(jù)源。
public class DsSpelExpressionProcessor extends DsProcessor {
/**
* 參數(shù)發(fā)現(xiàn)器
*/
private static final ParameterNameDiscoverer NAME_DISCOVERER = new DefaultParameterNameDiscoverer();
/**
* Express語法解析器
*/
private static final ExpressionParser PARSER = new SpelExpressionParser();
/**
* 解析上下文的模板
* 對于默認不設置的情況下,從參數(shù)中取值的方式 #param1
* 設置指定模板 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);
//默認使用第一個參數(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注解就可以實現(xiàn)數(shù)據(jù)源的動態(tài)切換了。
后續(xù)問題
在需要切換數(shù)據(jù)源的情況下,將對應數(shù)據(jù)源Key信息作為Mapper第一個參數(shù)傳遞
例如:
/**
* 調用不同的數(shù)據(jù)連接生成新的數(shù)據(jù)庫
* @param onlySignIp
* @param dbName
* @return
*/
@DS(value = "#onlySignIp")
public int createDataSource(String onlySignIp,@Param("dbName")String dbName);
- 在切換數(shù)據(jù)源的情況下必須要加上**@DS**注解,而且還必須將對應數(shù)據(jù)源的Key作為第一個參數(shù)進行傳入
小結
通過對 dynamic-datasource-spring-boot-starter 的剖析,我們簡單的實現(xiàn)了自定義創(chuàng)建數(shù)據(jù)源,切換數(shù)據(jù)源的操作,后續(xù)問題如果大家有對應的解決方法,希望評論告知
到此這篇關于SpringBoot多數(shù)據(jù)源解決方案:dynamic-datasource-spring-boot-starter的文章就介紹到這了,更多相關SpringBoot多數(shù)據(jù)源內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
- SpringBoot利用dynamic-datasource-spring-boot-starter解決多數(shù)據(jù)源問題
- springboot項目如何配置多數(shù)據(jù)源
- springboot多數(shù)據(jù)源實現(xiàn)方式
- SpringBoot實現(xiàn)多數(shù)據(jù)源配置的示例詳解
- SpringBoot框架DataSource多數(shù)據(jù)源配置方式
- SpringBoot 多數(shù)據(jù)源及事務解決方案小結
- springboot Jpa多數(shù)據(jù)源(不同庫)配置過程
- 淺析SpringBoot多數(shù)據(jù)源實現(xiàn)方案
相關文章
springboot與vue實現(xiàn)簡單的CURD過程詳析
這篇文章主要介紹了springboot與vue實現(xiàn)簡單的CURD過程詳析,圍繞springboot與vue的相關資料展開實現(xiàn)CURD過程的過程介紹,需要的小伙伴可以參考一下2022-01-01
Spring Junit測試找不到SpringJUnit4ClassRunner.class的解決
這篇文章主要介紹了Spring Junit測試找不到SpringJUnit4ClassRunner.class的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-04-04
Spring?Boot中使用Spring?Retry重試框架的操作方法
這篇文章主要介紹了Spring?Retry?在SpringBoot?中的應用,介紹了RetryTemplate配置的時候,需要設置的重試策略和退避策略,需要的朋友可以參考下2022-04-04
MyBatis-Plus?中?typeHandler?的使用實例詳解
本文介紹了在MyBatis-Plus中如何使用typeHandler處理json格式字段和自定義typeHandler,通過使用JacksonTypeHandler,可以簡單實現(xiàn)將實體類字段轉換為json格式存儲,感興趣的朋友跟隨小編一起看看吧2024-10-10

