多數(shù)據(jù)源配置
- 分包: 不同數(shù)據(jù)源的在不同的目錄下;事務(wù)的回滾需要創(chuàng)建根據(jù)數(shù)據(jù)源創(chuàng)建
- 注解
- AOP: aop注解切面需要在Service層進(jìn)行數(shù)據(jù)源切換;事務(wù)可以將多個數(shù)據(jù)源放在一個事務(wù)中;
分包形式
不同的數(shù)據(jù)源的sql操作分布在不同的路徑下
兩個數(shù)據(jù)源的basepackage分別為:
- com.yany.dao.multi.ads
- com.yany.dao.multi.rds
在創(chuàng)建MapperScannerConfigurer時霞捡,對應(yīng)不同的數(shù)據(jù)源掃描不同的basepackage路徑
mapperScannerConfigurer.setBasePackage("xxxx");
對應(yīng)的xml寇钉,即MAPPER_PATH
- classpath:/com/yany/mapper/multi/ads/**.xml
- classpath:/com/yany/mapper/multi/rds/**.xml
在創(chuàng)建SqlSessionFactoryBean時,MAPPER_PATH對應(yīng)分別對應(yīng)于ads和rds的sql路徑
sessionFactory.setMapperLocations(pathMatchingResourcePatternResolver.getResources(MAPPER_PATH));
在使用時舞蔽,不同數(shù)據(jù)源的操作在分別在不同的路徑創(chuàng)建即可怎诫。
注解形式
準(zhǔn)備好兩個注解類夭苗,分別對應(yīng)于兩個數(shù)據(jù)源:
public @interface RdsRepository {
}
public @interface AdsRepository {
}
同分包類似分別為Rds和Ads兩個數(shù)據(jù)源創(chuàng)建兩個SqlSessionFactoryBean和DataSourceTransactionManager跷睦,略微不同的是SqlSessionFactoryBean的setMapperLocations是允許相同路徑。
在創(chuàng)建兩個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è)置對應(yīng)不同的注解類
使用時匙睹,在不同數(shù)據(jù)源的dao接口上添加對應(yīng)的注解
@RdsRepository
public interface AnnotationRdsDao {
int selectCount();
}
@AdsRepository
public interface AnnotationAdsDao {
int selectCount();
}
而sql對應(yīng)的xml不變,對應(yīng)好namespace即可
AOP形式
創(chuàng)建一個動態(tài)數(shù)據(jù)源
- 創(chuàng)建數(shù)據(jù)源類型的枚舉類
public enum DatabaseType {
Ads, Rds
}
- 創(chuàng)建一個線程安全的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類為每一個線程都維護(hù)了自己獨(dú)有的變量拷貝济竹,每個線程都擁有了自己獨(dú)立的一個變量痕檬,避免并發(fā)問題。
- 創(chuàng)建動態(tài)數(shù)據(jù)源DynamicDataSource繼承AbstractRoutingDataSource
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return DatabaseContextHolder.getDatabaseType();
}
}
- 創(chuàng)建AOP對應(yīng)的MyBatis配置
創(chuà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以及這個和以前類似不再贅述送浊,具體看github上代碼
- 創(chuàng)建切邊
掃描對應(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í)例唁桩,動態(tài)切換數(shù)據(jù)源
System.out.println("IAdsAopService Aspect");
DatabaseContextHolder.setDatabaseType(DatabaseType.Ads);
} else {//連接點(diǎn)所屬的類實(shí)例是(當(dāng)然,這一步也可以不寫耸棒,因?yàn)閐efaultTargertDataSource就是該類所用的rdsDataSource)
System.out.println("IRdsAopService Aspect");
DatabaseContextHolder.setDatabaseType(DatabaseType.Rds);
}
}
}
上述切換規(guī)則比較簡單荒澡,具體可根據(jù)業(yè)務(wù)情況,包目錄結(jié)構(gòu)与殃,或者是類名規(guī)則等進(jìn)行解析切換单山。
具體代碼將github:https://github.com/yany8060/SpringDemo.git
博客:http://yany8060.xyz