mybatis多數(shù)據(jù)源動態(tài)切換的完整步驟
筆者主要從事c#開發(fā),近期因為項目需要,搭建了一套spring-cloud微服務(wù)框架,集成了eureka服務(wù)注冊中心、
gateway網(wǎng)關(guān)過濾、admin服務(wù)監(jiān)控、auth授權(quán)體系驗證,集成了redis、swagger、jwt、mybatis多數(shù)據(jù)源等各項功能。
具體搭建過程后續(xù)另寫播客介紹。具體結(jié)構(gòu)如下:

在搭建過程集成mybatis的時候,考慮到單一數(shù)據(jù)源無法滿足實際業(yè)務(wù)需要,故結(jié)合c#的開發(fā)經(jīng)驗,進行多數(shù)據(jù)源動態(tài)集成。
mybatis的多數(shù)據(jù)源可以采用兩種方式進行,第一種是分包方式實現(xiàn),這種方式靈活性不高,而且較為繁瑣,故不做過多介紹。
另一種方式是采用AOP的思想,進行注解動態(tài)切換,參考網(wǎng)上教程,核心思想是依靠 繼承AbstractRoutingDataSource,重寫determineCurrentLookupKey()方法,在該方法中使用DatabaseContextHolder獲取當(dāng)前線程的dataSource。
但是網(wǎng)上方法大都是首先定義好各個datasource,比如有三個數(shù)據(jù)源,就需要實現(xiàn)定義好三個datasource,筆者感覺這種方法,在我目前這套框架中不夠靈活,因為筆者采用的是微服務(wù)框架,考慮到各個服務(wù)都有可能使用不同的數(shù)據(jù)源,而多數(shù)據(jù)源動態(tài)切換是放在公共方法中實現(xiàn)的,如果每有新的數(shù)據(jù)源就要定義一個,對代碼的侵入性太高,在c#中,選擇數(shù)據(jù)源很容易,根據(jù)連接名稱就可以切換過去,如下所示:
<connectionStrings> <add name="test1" connectionString="server=127.0.0.1;user id=root;password=123456;database=db1;charset=utf8" providerName="MySql.Data.MySqlClient" /> <add name="test2" connectionString="server=127.0.0.1;user id=root;password=123456;database=db2;charset=utf8" providerName="MySql.Data.MySqlClient" /> <add name="test3" connectionString="server=127.0.0.1;user id=root;password=123456;database=db3;charset=utf8" providerName="MySql.Data.MySqlClient" /> <connectionStrings>
能不能像c#這樣根據(jù)連接名稱就自動選擇呢,筆者的連接配置如下所示:
spring: application: name: csg-auth datasource: kbase: - driverClassName: com.kbase.jdbc.Driver jdbcUrl: jdbc:kbase://127.0.0.1 username: DBOWN password: jdbc: - driverClassName: com.mysql.cj.jdbc.Driver jdbcUrl: jdbc:mysql://localhost:3306/nacos?serverTimezone=GMT%2B8&useUnicode=false&characterEncoding=utf8&useSSL=false username: root password: 123456 connName: nacos - driverClassName: com.mysql.cj.jdbc.Driver jdbcUrl: jdbc:mysql://localhost:3306/tpi?serverTimezone=GMT%2B8&useUnicode=false&characterEncoding=utf8&useSSL=false username: root password: 123456 connName: tpi
其中kbase不用理會,是我們公司自己的數(shù)據(jù)庫,jdbc是維護的連接集合,其中connName就是我們自定義的連接名稱,
根據(jù)connName就可以自動切換到對應(yīng)數(shù)據(jù)源。
筆者實現(xiàn)代碼如下:

第一步
首先,編寫DynamicDataSource類集成AbstractRoutingDataSource,重寫determineCurrentLookupKey方法,該方法主要作用是選擇數(shù)據(jù)源的key
代碼如下:
/**
* 動態(tài)數(shù)據(jù)源
* */
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return DataSourceHolder.getDataSource();
}
}
第二步
第二部編寫DataSourceHolder類,提供設(shè)置、獲取、情況數(shù)據(jù)源的方法,如下所示:
public class DataSourceHolder {
/**
* 線程本地環(huán)境
*/
private static final ThreadLocal<String> dataSources = new ThreadLocal<String>();
/**
* 設(shè)置數(shù)據(jù)源
*/
public static void setDataSources(String connName) {
dataSources.set(connName);
}
/**
* 獲取數(shù)據(jù)源
*/
public static String getDataSource() {
return dataSources.get();
}
/**
* 清楚數(shù)據(jù)源
*/
public static void clearDataSource() {
dataSources.remove();
}
}
第三步
第三步,編寫DataSourceConfig類,該類主要作用是讀取配置文件中的數(shù)據(jù)源連接集合,以及維護項目數(shù)據(jù)源的Bean對象,
代碼如下:
@Component
@ConfigurationProperties("spring.datasource")
public class DataSourceConfig {
private List<DataSourceModel> jdbc;
public Map<Object, Object> getDataSourceMap(){
Map<Object, Object>map=new HashMap<>();
if (jdbc!=null&&jdbc.size()>0){
for (int i = 0; i < jdbc.size() ; i++) {
DataSourceBuilder dataSourceBuilder=DataSourceBuilder.create();
dataSourceBuilder.driverClassName(jdbc.get(i).getDriverClassName());
dataSourceBuilder.password(jdbc.get(i).getPassword());
dataSourceBuilder.username(jdbc.get(i).getUsername());
dataSourceBuilder.url(jdbc.get(i).getJdbcUrl());
map.put(jdbc.get(i).getConnName(),dataSourceBuilder.build());
}
}
return map;
}
@Bean
public DataSource csgDataSource(){
DynamicDataSource dynamicDataSource=new DynamicDataSource();
Map<Object,Object>dataSourceMap=getDataSourceMap();
dynamicDataSource.setTargetDataSources(dataSourceMap);
Object object= dataSourceMap.values().toArray()[0];
dynamicDataSource.setDefaultTargetDataSource(object);
return dynamicDataSource;
}
public void setJdbc(List<DataSourceModel> jdbc) {
this.jdbc = jdbc;
}
public List<DataSourceModel> getJdbc(){
return this.jdbc;
}
}
其中,getDataSourceMap()方法,作用是根據(jù)配置的連接集合,生成AbstractRoutingDataSource所需要的resolvedDataSources。
而csgDataSource()方法,添加了@Bean注解,作用是讓mybatis的SqlSessionFactory,能夠使用咱們維護的數(shù)據(jù)源。
第四部
編寫MyBatisConfig類,該類主要作用是 配置好mybatis的數(shù)據(jù)源。
@Configuration
public class MyBatisConfig {
@Autowired
private DataSource csgDataSource;
@Bean
public SqlSessionFactory sqlSessionFactory() throws Exception {
SqlSessionFactoryBean sqlSessionFactoryBean=new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(csgDataSource);
sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver()
.getResources("classpath:mapper/**/*.xml"));
return sqlSessionFactoryBean.getObject();
}
@Bean
public PlatformTransactionManager platformTransactionManager(){
return new DataSourceTransactionManager(csgDataSource);
}
}
可以看到,這里選擇的是我們定義好的csgDataSource,其作用也是如此。
第五步
編寫TargetDataSource注解
/**
* 注解標(biāo)簽
* 作用于 方法、接口、類、枚舉、注解
* */
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD,ElementType.TYPE})
public @interface TargetDataSource {
String connName();
}
其中connName,就是我們需要使用的數(shù)據(jù)源
第六步
編寫DataSourceExchange,改類為切面,作用于TargetDataSource注解,故使用TargetDataSource注解的時候,
會根據(jù)connName自動選擇數(shù)據(jù)源。
@Aspect
@Component
public class DataSourceExchange {
@Before("@annotation(TargetDataSource)")
public void before(JoinPoint joinPoint){
MethodSignature sign = (MethodSignature) joinPoint.getSignature();
Method method = sign.getMethod();
boolean isMethodAop= method.isAnnotationPresent(TargetDataSource.class);
if (isMethodAop){
TargetDataSource datasource = method.getAnnotation(TargetDataSource.class);
DataSourceHolder.setDataSources(datasource.connName());
}else {
if (joinPoint.getTarget().getClass().isAnnotationPresent(TargetDataSource.class)){
TargetDataSource datasource = joinPoint.getTarget().getClass().getAnnotation(TargetDataSource.class);
DataSourceHolder.setDataSources(datasource.connName());
}
}
}
@After("@annotation(TargetDataSource)")
public void after(){
DataSourceHolder.clearDataSource();
}
}
改切面作用于方法運行前后,負(fù)責(zé)選擇、取消數(shù)據(jù)源。
第七部
開始驗證,使用方法如下:
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
@Override
@TargetDataSource(connName = "nacos")
public List<UserEntity> getList() {
List<UserEntity> list= userMapper.selectUserList();
return list;
}
}
在service中,在需要進行數(shù)據(jù)庫操作的方法上,添加TargetDataSource注解,即可自動切換到所需要的數(shù)據(jù)源。
至此,mybatis就可以動態(tài)切換數(shù)據(jù)源了。
筆者從事java開發(fā)工作不多,改方法可能不是太好,也請各位看官勿噴~
總結(jié)
到此這篇關(guān)于mybatis多數(shù)據(jù)源動態(tài)切換的文章就介紹到這了,更多相關(guān)mybatis多數(shù)據(jù)源動態(tài)切換內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Spring+Mybatis動態(tài)切換數(shù)據(jù)源的方法
- Spring與Mybatis相結(jié)合實現(xiàn)多數(shù)據(jù)源切換功能
- spring boot+mybatis 多數(shù)據(jù)源切換(實例講解)
- Spring + Mybatis 項目實現(xiàn)動態(tài)切換數(shù)據(jù)源實例詳解
- 關(guān)于Spring3 + Mybatis3整合時多數(shù)據(jù)源動態(tài)切換的問題
- SpringMVC Mybatis配置多個數(shù)據(jù)源并切換代碼詳解
- spring boot + mybatis實現(xiàn)動態(tài)切換數(shù)據(jù)源實例代碼
- SpringBoot Mybatis動態(tài)數(shù)據(jù)源切換方案實現(xiàn)過程
- Mybatis多數(shù)據(jù)源切換實現(xiàn)代碼
- Spring AOP如何實現(xiàn)注解式的Mybatis多數(shù)據(jù)源切換詳解
相關(guān)文章
Java中關(guān)于isEmpty方法、null以及““的區(qū)別
這篇文章主要介紹了Java中關(guān)于isEmpty方法、null以及““的區(qū)別,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-08-08
Spring?Data?Elasticsearch?5.0.x修改數(shù)據(jù)后無法立即刷新解決方法示例
這篇文章主要為大家介紹了Spring?Data?Elasticsearch?5.0.x修改數(shù)據(jù)后無法立即刷新解決方法示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-08-08
Java自定義注解實現(xiàn)數(shù)據(jù)脫敏
在實際開發(fā)中經(jīng)常會遇到有一些信息不能全部展示用戶,需要隱藏(可以叫脫敏),所以本文為大家分享了利用自定義注解實現(xiàn)數(shù)據(jù)脫敏的示例代碼,需要的可以參考下2023-07-07
SpringBoot @value注解動態(tài)刷新問題小結(jié)
@Value注解 所對應(yīng)的數(shù)據(jù)源來自項目的 Environment 中,我們可以將數(shù)據(jù)庫或其他文件中的數(shù)據(jù),加載到項目的 Environment 中,然后 @Value注解 就可以動態(tài)獲取到配置信息了,這篇文章主要介紹了SpringBoot @value注解動態(tài)刷新,需要的朋友可以參考下2023-09-09
IntelliJ Plugin 開發(fā)之添加第三方j(luò)ar的示例代碼
這篇文章主要介紹了IntelliJ Plugin 開發(fā)之添加第三方j(luò)ar的示例代碼,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-09-09

