mybatis 多數(shù)據(jù)源動態(tài)切換
點擊上方藍色字體,選擇“標(biāo)星公眾號”
優(yōu)質(zhì)文章,第一時間送達
? 作者?|??張教主
來源 |? urlify.cn/73imQb
筆者主要從事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ù)連接名
稱就可以切換過去,
如下所示:
"test1"?connectionString="server=127.0.0.1;user?id=root;password=123456;database=db1;charset=utf8"?providerName="MySql.Data.MySqlClient"?/>
"test2"?connectionString="server=127.0.0.1;user?id=root;password=123456;database=db2;charset=utf8"?providerName="MySql.Data.MySqlClient"?/>
"test3"?connectionString="server=127.0.0.1;user?id=root;password=123456;database=db3;charset=utf8"?providerName="MySql.Data.MySqlClient"?/>
能不能像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?dataSources?=?new?ThreadLocal();
????/**
?????*?設(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?jdbc;
????public?Map 其中,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?getList()?{
????????List?list=?userMapper.selectUserList();
????????return?list;
????}
}
在service中,在需要進行數(shù)據(jù)庫操作的方法上,添加TargetDataSource注解,即可自動切換到所需要的數(shù)據(jù)源。
至此,mybatis就可以動態(tài)切換數(shù)據(jù)源了。
粉絲福利:實戰(zhàn)springboot+CAS單點登錄系統(tǒng)視頻教程免費領(lǐng)取
???
?長按上方微信二維碼?2 秒 即可獲取資料
感謝點贊支持下哈?

