多数据源配置-基于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);
    }
}