您好,登錄后才能下訂單哦!
使用mybatis如何實(shí)現(xiàn)動(dòng)態(tài)切換多數(shù)據(jù)源?相信很多沒(méi)有經(jīng)驗(yàn)的人對(duì)此束手無(wú)策,為此本文總結(jié)了問(wèn)題出現(xiàn)的原因和解決方法,通過(guò)這篇文章希望你能解決這個(gè)問(wèn)題。
具體結(jié)構(gòu)如下:
在搭建過(guò)程集成mybatis的時(shí)候,考慮到單一數(shù)據(jù)源無(wú)法滿(mǎn)足實(shí)際業(yè)務(wù)需要,故結(jié)合c#的開(kāi)發(fā)經(jīng)驗(yàn),進(jìn)行多數(shù)據(jù)源動(dòng)態(tài)集成。
mybatis的多數(shù)據(jù)源可以采用兩種方式進(jìn)行,第一種是分包方式實(shí)現(xiàn),這種方式靈活性不高,而且較為繁瑣,故不做過(guò)多介紹。
另一種方式是采用AOP的思想,進(jìn)行注解動(dòng)態(tài)切換,參考網(wǎng)上教程,核心思想是依靠 繼承AbstractRoutingDataSource,重寫(xiě)determineCurrentLookupKey()方法,在該方法中使用DatabaseContextHolder獲取當(dāng)前線(xiàn)程的dataSource。
但是網(wǎng)上方法大都是首先定義好各個(gè)datasource,比如有三個(gè)數(shù)據(jù)源,就需要實(shí)現(xiàn)定義好三個(gè)datasource,筆者感覺(jué)這種方法,在我目前這套框架中不夠靈活,因?yàn)楣P者采用的是微服務(wù)框架,考慮到各個(gè)服務(wù)都有可能使用不同的數(shù)據(jù)源,而多數(shù)據(jù)源動(dòng)態(tài)切換是放在公共方法中實(shí)現(xiàn)的,如果每有新的數(shù)據(jù)源就要定義一個(gè),對(duì)代碼的侵入性太高,在c#中,選擇數(shù)據(jù)源很容易,根據(jù)連接名稱(chēng)就可以切換過(guò)去,如下所示:
<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ù)連接名稱(chēng)就自動(dòng)選擇呢,筆者的連接配置如下所示:
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不用理會(huì),是我們公司自己的數(shù)據(jù)庫(kù),jdbc是維護(hù)的連接集合,其中connName就是我們自定義的連接名稱(chēng),
根據(jù)connName就可以自動(dòng)切換到對(duì)應(yīng)數(shù)據(jù)源。
筆者實(shí)現(xiàn)代碼如下:
第一步
首先,編寫(xiě)DynamicDataSource類(lèi)集成AbstractRoutingDataSource,重寫(xiě)determineCurrentLookupKey方法,該方法主要作用是選擇數(shù)據(jù)源的key
代碼如下:
/** * 動(dòng)態(tài)數(shù)據(jù)源 * */ public class DynamicDataSource extends AbstractRoutingDataSource { @Override protected Object determineCurrentLookupKey() { return DataSourceHolder.getDataSource(); } }
第二步
第二部編寫(xiě)DataSourceHolder類(lèi),提供設(shè)置、獲取、情況數(shù)據(jù)源的方法,如下所示:
public class DataSourceHolder { /** * 線(xiàn)程本地環(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(); } }
第三步
第三步,編寫(xiě)DataSourceConfig類(lèi),該類(lèi)主要作用是讀取配置文件中的數(shù)據(jù)源連接集合,以及維護(hù)項(xiàng)目數(shù)據(jù)源的Bean對(duì)象,
代碼如下:
@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,能夠使用咱們維護(hù)的數(shù)據(jù)源。
第四部
編寫(xiě)MyBatisConfig類(lèi),該類(lèi)主要作用是 配置好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,其作用也是如此。
第五步
編寫(xiě)TargetDataSource注解
/** * 注解標(biāo)簽 * 作用于 方法、接口、類(lèi)、枚舉、注解 * */ @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD,ElementType.TYPE}) public @interface TargetDataSource { String connName(); }
其中connName,就是我們需要使用的數(shù)據(jù)源
第六步
編寫(xiě)DataSourceExchange,改類(lèi)為切面,作用于TargetDataSource注解,故使用TargetDataSource注解的時(shí)候,
會(huì)根據(jù)connName自動(dòng)選擇數(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(); } }
改切面作用于方法運(yùn)行前后,負(fù)責(zé)選擇、取消數(shù)據(jù)源。
第七部
開(kāi)始驗(yàn)證,使用方法如下:
@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中,在需要進(jìn)行數(shù)據(jù)庫(kù)操作的方法上,添加TargetDataSource注解,即可自動(dòng)切換到所需要的數(shù)據(jù)源。
至此,mybatis就可以動(dòng)態(tài)切換數(shù)據(jù)源了。
看完上述內(nèi)容,你們掌握使用mybatis如何實(shí)現(xiàn)動(dòng)態(tài)切換多數(shù)據(jù)源的方法了嗎?如果還想學(xué)到更多技能或想了解更多相關(guān)內(nèi)容,歡迎關(guān)注億速云行業(yè)資訊頻道,感謝各位的閱讀!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。