SpringBoot配置多数据源
配置多数据源
spring:
datasource:
ds1: //数据源1
jdbc-url: jdbc:mysql://IP:端口/库名?autoReconnect=true&autoReconnectForPools=true&useUnicode=true&characterEncoding=UTF8&useSSL=false&serverTimezone=GMT%2B8
username: username
password: password
driver-class-name: com.mysql.cj.jdbc.Driver
ds2: //数据源2
jdbc-url: jdbc:mysql://IP:端口/库名?autoReconnect=true&autoReconnectForPools=true&useUnicode=true&characterEncoding=UTF8&useSSL=false&serverTimezone=GMT%2B8
username: username
password: password
driver-class-name: com.mysql.cj.jdbc.Driver
创建数据源枚举
public enum DataSourceType {
/**
* 主库
*/
DS1,
/**
* 从库
*/
DS2
}
配置数据源切换
创建一个数据源切换处理类,有对数据源变量的获取、设置和清空的方法。其中的ThreadLocal用于保存某个线程共享变量。
@Slf4j
public class DynamicDataSourceContextHolder {
/**
* 使用ThreadLocal维护变量,ThreadLocal为每个使用该变量的线程提供独立的变量副本,
* 所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。
*/
private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>();
/**
* 设置数据源变量
* @param dataSourceType
*/
public static void setDataSourceType(String dataSourceType){
log.info("切换到{}数据源", dataSourceType);
CONTEXT_HOLDER.set(dataSourceType);
}
/**
* 获取数据源变量
* @return
*/
public static String getDataSourceType(){
return CONTEXT_HOLDER.get();
}
/**
* 清空数据源变量
*/
public static void clearDataSourceType(){
CONTEXT_HOLDER.remove();
}
}
编写动态数据源
动态切换数据源主要依靠AbstractRoutingDataSource。defaultTargetDataSource默认目标数据源,targetDataSources(map类型)存放用来切换的数据源。
public class DynamicDataSource extends AbstractRoutingDataSource {
public DynamicDataSource(DataSource defaultTargetDataSource, Map<Object, Object> targetDataSources) {
super.setDefaultTargetDataSource(defaultTargetDataSource);
super.setTargetDataSources(targetDataSources);
// afterPropertiesSet()方法调用时用来将targetDataSources的属性写入resolvedDataSources中的
super.afterPropertiesSet();
}
/**
* 根据Key获取数据源的信息
*
* @return
*/
@Override
protected Object determineCurrentLookupKey() {
return DynamicDataSourceContextHolder.getDataSourceType();
}
}
数据源注入
/**
* 多数据源配置
*/
@Configuration
public class DataSourceConfig {
@Bean("ds1DataSource")
@ConfigurationProperties("spring.datasource.ds1")
public DataSource ds1DataSource() {
return DataSourceBuilder.create().build();
}
@Bean("ds2DataSource")
@ConfigurationProperties("spring.datasource.ds2")
public DataSource ds2DataSource() {
return DataSourceBuilder.create().build();
}
@Bean(name = "dynamicDataSource")
@Primary
public DynamicDataSource dataSource() {
Map<Object, Object> targetDataSources = new HashMap<>();
targetDataSources.put(DataSourceType.DS1.name(), ds1DataSource());
targetDataSources.put(DataSourceType.DS2.name(), ds2DataSource());
return new DynamicDataSource(ds1DataSource(), targetDataSources);
}
}
自定义多数据源切换注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataSource {
/**
* 切换数据源名称
*/
DataSourceType value() default DataSourceType.DS1;
}
AOP拦截类实现数据源的切换
通过拦截上面的注解,在方法执行之前设置配置的数据源,DynamicDataSourceContextHolder.setDataSourceType(dataSource.value().name());这里的数据源信息从我们的注解上面获取,如果没有就是用默认的数据源。
@Slf4j
@Aspect
@Order(1)
@Component
public class DataSourceAspect {
@Pointcut("@annotation(com.console.config.datasource.DataSource)")
public void dsPointCut() {
}
@Around("dsPointCut()")
public Object around(ProceedingJoinPoint point) throws Throwable {
MethodSignature signature = (MethodSignature) point.getSignature();
Method method = signature.getMethod();
//获取使用该注解的方法
DataSource dataSource = method.getAnnotation(DataSource.class);
if (dataSource != null) {
//设置数据源
DynamicDataSourceContextHolder.setDataSourceType(dataSource.value().name());
}
try {
return point.proceed();
} finally {
// 销毁数据源 在执行方法之后
DynamicDataSourceContextHolder.clearDataSourceType();
}
}
}
这里使用@Around,在调用目标方法前,进行aop拦截,通过解析注解上的值来切换数据源。在调用方法结束后,清除数据源。
使用注解切换数据源
//在Mapper接口方法上使用
@DataSource(value = DataSourceType.DS2)
@Select("select * from xxx ")
DTO methodName();
运行该方法时,会调用切面,修改数据源,从而完成数据源的切换。