多數(shù)據(jù)源配置
- 分包: 不同數(shù)據(jù)源的在不同的目錄下;事務(wù)的回滾需要?jiǎng)?chuàng)建根據(jù)數(shù)據(jù)源創(chuàng)建
- 注解
- AOP: aop注解切面需要在Service層進(jìn)行數(shù)據(jù)源切換;事務(wù)可以將多個(gè)數(shù)據(jù)源放在一個(gè)事務(wù)中;
分包形式
不同的數(shù)據(jù)源的sql操作分布在不同的路徑下
兩個(gè)數(shù)據(jù)源的basepackage分別為:
- com.yany.dao.multi.ads
- com.yany.dao.multi.rds
在創(chuàng)建MapperScannerConfigurer時(shí),對(duì)應(yīng)不同的數(shù)據(jù)源掃描不同的basepackage路徑
mapperScannerConfigurer.setBasePackage("xxxx");
對(duì)應(yīng)的xml,即MAPPER_PATH
- classpath:/com/yany/mapper/multi/ads/**.xml
- classpath:/com/yany/mapper/multi/rds/**.xml
在創(chuàng)建SqlSessionFactoryBean時(shí),MAPPER_PATH對(duì)應(yīng)分別對(duì)應(yīng)于ads和rds的sql路徑
sessionFactory.setMapperLocations(pathMatchingResourcePatternResolver.getResources(MAPPER_PATH));
在使用時(shí),不同數(shù)據(jù)源的操作在分別在不同的路徑創(chuàng)建即可。
注解形式
準(zhǔn)備好兩個(gè)注解類,分別對(duì)應(yīng)于兩個(gè)數(shù)據(jù)源:
public @interface RdsRepository {
}
public @interface AdsRepository {
}
同分包類似分別為Rds和Ads兩個(gè)數(shù)據(jù)源創(chuàng)建兩個(gè)SqlSessionFactoryBean和DataSourceTransactionManager,略微不同的是SqlSessionFactoryBean的setMapperLocations是允許相同路徑。
在創(chuàng)建兩個(gè)MapperScannerConfigurer
/**
* 以注解的方式 進(jìn)行多數(shù)據(jù)源配置
*
* @return
*/
@Bean
public MapperScannerConfigurer createAnnotatationAdsMapperScannerConfigurer() {
MapperScannerConfigurer mapperScannerConfigurer = new MapperScannerConfigurer();
mapperScannerConfigurer.setBasePackage("com.yany.dao.multi.annotation");
mapperScannerConfigurer.setSqlSessionFactoryBeanName("annotationAdsSqlSessionFactory");
mapperScannerConfigurer.setAnnotationClass(AdsRepository.class);
return mapperScannerConfigurer;
}
/**
* 以注解的方式 進(jìn)行多數(shù)據(jù)源配置
*
* @return
*/
@Bean
public MapperScannerConfigurer createAnnotatationRdsMapperScannerConfigurer() {
MapperScannerConfigurer mapperScannerConfigurer = new MapperScannerConfigurer();
mapperScannerConfigurer.setBasePackage("com.yany.dao.multi.annotation");
mapperScannerConfigurer.setSqlSessionFactoryBeanName("annotationRdsSqlSessionFactory");
mapperScannerConfigurer.setAnnotationClass(RdsRepository.class);
return mapperScannerConfigurer;
}
上述代碼和以前的主要的區(qū)別在:setAnnotationClass設(shè)置對(duì)應(yīng)不同的注解類
使用時(shí),在不同數(shù)據(jù)源的dao接口上添加對(duì)應(yīng)的注解
@RdsRepository
public interface AnnotationRdsDao {
int selectCount();
}
@AdsRepository
public interface AnnotationAdsDao {
int selectCount();
}
而sql對(duì)應(yīng)的xml不變,對(duì)應(yīng)好namespace即可
AOP形式
創(chuàng)建一個(gè)動(dòng)態(tài)數(shù)據(jù)源
- 創(chuàng)建數(shù)據(jù)源類型的枚舉類
public enum DatabaseType {
Ads, Rds
}
- 創(chuàng)建一個(gè)線程安全的DatabaseType容器
public class DatabaseContextHolder {
private final static ThreadLocal<DatabaseType> contextHolder = new ThreadLocal<>();
public static DatabaseType getDatabaseType() {
return contextHolder.get();
}
public static void setDatabaseType(DatabaseType type) {
contextHolder.set(type);
}
}
ThreadLocal類為每一個(gè)線程都維護(hù)了自己獨(dú)有的變量拷貝,每個(gè)線程都擁有了自己獨(dú)立的一個(gè)變量,避免并發(fā)問(wèn)題。
- 創(chuàng)建動(dòng)態(tài)數(shù)據(jù)源DynamicDataSource繼承AbstractRoutingDataSource
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return DatabaseContextHolder.getDatabaseType();
}
}
- 創(chuàng)建AOP對(duì)應(yīng)的MyBatis配置
創(chuàng)建動(dòng)態(tài)數(shù)據(jù)源的bean
@Bean
public DynamicDataSource setDataSource() {
Map<Object, Object> targetDataSources = new HashMap<>();
targetDataSources.put(DatabaseType.Ads, adsDataSource);
targetDataSources.put(DatabaseType.Rds, rdsDataSource);
DynamicDataSource dataSource = new DynamicDataSource();
dataSource.setTargetDataSources(targetDataSources);
dataSource.setDefaultTargetDataSource(rdsDataSource);// 默認(rèn)的datasource設(shè)置為rdsDataSource
return dataSource;
}
創(chuàng)建SqlSessionFactoryBean和DataSourceTransactionManager以及這個(gè)和以前類似不再贅述,具體看github上代碼
- 創(chuàng)建切邊
掃描對(duì)應(yīng)的Service層,在執(zhí)行具體的服務(wù)代碼前,根據(jù)調(diào)用的Service類進(jìn)行數(shù)據(jù)源的切換。
@Aspect
@Component
public class DataSourceAspect {
/**
* 使用空方法定義切點(diǎn)表達(dá)式
*/
@Pointcut("execution(* com.yany.service.**.*(..))")
public void declareJointPointExpression() {
}
@Before("declareJointPointExpression()")
public void setDataSourceKey(JoinPoint point) {
if (point.getTarget() instanceof IAdsAopService ||
point.getTarget() instanceof AdsAopServiceImpl) {
//根據(jù)連接點(diǎn)所屬的類實(shí)例,動(dòng)態(tài)切換數(shù)據(jù)源
System.out.println("IAdsAopService Aspect");
DatabaseContextHolder.setDatabaseType(DatabaseType.Ads);
} else {//連接點(diǎn)所屬的類實(shí)例是(當(dāng)然,這一步也可以不寫(xiě),因?yàn)閐efaultTargertDataSource就是該類所用的rdsDataSource)
System.out.println("IRdsAopService Aspect");
DatabaseContextHolder.setDatabaseType(DatabaseType.Rds);
}
}
}
上述切換規(guī)則比較簡(jiǎn)單,具體可根據(jù)業(yè)務(wù)情況,包目錄結(jié)構(gòu),或者是類名規(guī)則等進(jìn)行解析切換。
具體代碼將github:https://github.com/yany8060/SpringDemo.git
博客:http://yany8060.xyz