多数据源配置-基于Spring JDBC
一、基于注解配置自动切换
实现思路:
- 可以配置多个数据源,给不同的xml文件指定不同的数据源,这种方式只能实现文件级别的固定数据源配置,使用起来不方便
- 使用扩展
org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource
实现动态切换的数据源
1.1 Mybatis配置
1.2 相关Java逻辑
public class DataSourceHandle {
private DataSourceHandle() {
throw new IllegalStateException("Utility class");
}
private static final ThreadLocal contextHolder = new ThreadLocal<>();
public static void setDataSourceType(String dataSourceType) {
contextHolder.set(dataSourceType);
}
public static String getDataSourceType() {
return contextHolder.get();
}
public static void clearDataSourceType() {
contextHolder.remove();
}
}
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
// 在进行DAO操作前,通过上下文环境变量,获得数据源的类型
return DataSourceHandle.getDataSourceType();
}
}
@Aspect
@Order(-1)
@Component
public class MultipleDataSourceAspectAdvice {
private static final Logger log = LoggerFactory.getLogger(MultipleDataSourceAspectAdvice.class);
@Around("execution(* com.test.service..*(..))")
public void changeDataSourceAndProcess(ProceedingJoinPoint jp) throws Throwable {
// 暂存当前数据库类型
String beforeDataSourceType = DataSourceHandle.getDataSourceType();
MethodSignature msig = (MethodSignature) jp.getSignature();
try {
Class<?> targetClass = msig.getDeclaringType();
Method targetMethod = targetClass.getDeclaredMethod(msig.getName(), msig.getMethod().getParameterTypes());
TargetDataSource ds = targetMethod.getAnnotation(TargetDataSource.class);
if (ds == null) {
ds = targetClass.getAnnotation(TargetDataSource.class);
}
String dbTypeCode = (ds == null) ? null : ds.value().getCode();
if (log.isDebugEnabled()) {
log.debug("changeDataSource: {} -> {}", msig, dbTypeCode);
}
// 切换数据库类型
DataSourceHandle.setDataSourceType(dbTypeCode);
// 处理当前业务
jp.proceed();
} finally {
if (log.isDebugEnabled()) {
log.debug("restoreDataSource: {} -> {}", msig, beforeDataSourceType);
}
// 切换回当前的数据库类型
DataSourceHandle.setDataSourceType(beforeDataSourceType);
}
}
@Around("@within(ds) || @annotation(ds)")
public void changeDataSourceAndProcess(ProceedingJoinPoint jp, TargetDataSource ds) throws Throwable {
// 暂存当前数据库类型
String beforeDataSourceType = DataSourceHandle.getDataSourceType();
try {
if (ds == null) {
Class<?> targetClass = jp.getSignature().getDeclaringType();
// 方法上没有注解则取类的
ds = targetClass.getAnnotation(TargetDataSource.class);
}
String dbTypeCode = (ds == null) ? null : ds.value().getCode();
if (log.isDebugEnabled()) {
log.debug("changeDataSource: {} -> {}", jp.getSignature(), dbTypeCode);
}
// 切换数据库类型
DataSourceHandle.setDataSourceType(dbTypeCode);
// 处理当前业务
jp.proceed();
} finally {
if (log.isDebugEnabled()) {
log.debug("restoreDataSource: {} -> {}", jp.getSignature(), beforeDataSourceType);
}
// 切换回当前的数据库类型
DataSourceHandle.setDataSourceType(beforeDataSourceType);
}
}
}
@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TargetDataSource {
String value();
}
// 在相关service方法上指定需要切换的数据源
@TargetDataSource("ds2")
public void showAllTablesDb2() {
List tables = testMapper.selectAllTables();
log.info("---------------- tables: {}", tables);
}
二、各数据源独立配置
实现思路:
- 按目录固定分配数据源
- 每个数据源都有各自的事务管理器,相当于又重复配置了一遍(只是数据源和dao目录不同)
@Configuration
@MapperScan(basePackages = "com.worley.base.dao.cdm", sqlSessionTemplateRef = "cdmSqlSessionTemplate")
public class CdmDataSourceConfig {
@Bean(name = "cdmDataSourceProperties")
@ConfigurationProperties(prefix = "spring.datasource.cdm")
public DataSourceProperties cdmDataSourceProperties() {
return new DataSourceProperties();
}
@Bean(name = "cdmDataSource")
public DataSource cdmDataSource(@Qualifier("cdmDataSourceProperties") DataSourceProperties dataSourceProperties) {
return dataSourceProperties.initializeDataSourceBuilder().build();
}
@Bean("cdmSqlSessionFactory")
public SqlSessionFactory cdmSqlSessionFactory(@Qualifier("cdmDataSource") DataSource dataSource) throws Exception {
SqlSessionFactoryBean sqlSessionFactory = new SqlSessionFactoryBean();
sqlSessionFactory.setDataSource(dataSource);
sqlSessionFactory.setMapperLocations(
new PathMatchingResourcePatternResolver().getResources("classpath*:mapping/cdm/**/*.xml"));
sqlSessionFactory.setPlugins(new TableSegInterceptor());
// 下划线转驼峰
Objects.requireNonNull(sqlSessionFactory.getObject()).getConfiguration().setMapUnderscoreToCamelCase(true);
return sqlSessionFactory.getObject();
}
@Bean(name = "cdmTransactionManager")
public DataSourceTransactionManager cdmTransactionManager(@Qualifier("cdmDataSource") DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
@Bean(name = "cdmSqlSessionTemplate")
public SqlSessionTemplate cdmSqlSessionTemplate(
@Qualifier("cdmSqlSessionFactory") SqlSessionFactory sqlSessionFactory) {
return new SqlSessionTemplate(sqlSessionFactory);
}
}