动态数据源切换即多数据源切换,由于业务的需要或者历史的遗留等原因,一个项目中配置了多个数据库,用于查询不同类型的数据,因此我们就需要经常在各个库中切换数据源,接下来我们将进行具体的说明:
项目结构如下:
相关类说明:
DynamicDataSource:动态获取数据源的实现,继承AbstractRoutingDataSource(每执行一次数据库,动态获取DataSource)
DynamicDataSourceContextHolder:动态数据源上下文管理,相当于在容器中管理数据源实例
DynamicDattaSourceAspect:动态数据源通知
TargetDataSource:数据源注解,作用于类、接口或者方法上,用于指定数据源
DynamicDatasourceConfig:动态数据源配置,实例化所有配置数据源
具体类实现:
DynamicDataSource
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return DynamicDataSourceContextHolder.getDataSourceType();
}
}
DynamicDataSourceContextHolder:
/**
* 存放当前线程使用的数据源类型信息
*/
private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>();
/**
* 存放数据源id,即数据源实例名称
*/
public static List<String> dataSourceIds = new ArrayList<String>();
/**
* 设置数据源
*
* @param dataSourceType
*/
public static void setDataSourceType(String dataSourceType) {
logger.info("添加数据源实例到管理器中,dataSourceType{}", dataSourceType);
contextHolder.set(dataSourceType);
}
/**
* 获取数据源
*
* @return
*/
public static String getDataSourceType() {
logger.info("从数据源实例管理器中获取当前实例");
return contextHolder.get();
}
/**
* 清除数据源
*/
public static void clearDataSourceType() {
logger.info("清除当前数据源实例");
contextHolder.remove();
}
/**
* 判断当前数据源是否存在
*
* @param dataSourceId
* @return
*/
public static boolean isContainsDataSource(String dataSourceId) {
logger.info("判断当前数据源是否存在,dataSourceId={}", dataSourceId);
return dataSourceIds.contains(dataSourceId);
}
DynamicDattaSourceAspect
package com.xiaofeng.datasource2.aspect;
import com.xiaofeng.datasource2.aspect.annotation.TargetDataSource;
import com.xiaofeng.datasource2.dynamic.DynamicDataSourceContextHolder;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
/**
* @author xiaofeng
* @version V1.0
* @title: DynamicDattaSourceAspect
* @package: com.xiaofeng.sys.dynamic
* @description: 动态数据源通知
* @date 2019/8/28 16:48
*/
@Aspect
@Order(-1)
@Component
public class DynamicDattaSourceAspect {
private Logger logger = LoggerFactory.getLogger(DynamicDattaSourceAspect.class);
/**
* 改变数据源,判断使用注解中的数据源实例名称,根据实例名称从上下文管理器中获取数据源
*
* @param joinPoint
* @param targetDataSource
*/
@Before("@annotation(targetDataSource)")
public void changeDataSource(JoinPoint joinPoint, TargetDataSource targetDataSource) {
logger.info("选择数据源---" + targetDataSource.value().getValue());
DynamicDataSourceContextHolder.setDataSourceType(targetDataSource.value().getValue());
}
/**
* 使用完后清理数据源
*
* @param joinPoint
* @param targetDataSource
*/
@After("@annotation(targetDataSource)")
public void clearDataSource(JoinPoint joinPoint, TargetDataSource targetDataSource) {
logger.debug("清除数据源 " + targetDataSource.value().getValue() + " !");
DynamicDataSourceContextHolder.clearDataSourceType();
}
}
TargetDataSource
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TargetDataSource {
DataSourceEnum value() default DataSourceEnum.MASTER;
}
DynamicDatasourceConfig
@Bean(name = "master")
@ConfigurationProperties(prefix = "spring.datasource.druid.master")
public DataSource master() {
return DruidDataSourceBuilder.create().build();
}
@Bean(name = "slave")
@ConfigurationProperties(prefix = "spring.datasource.druid.slave")
public DataSource slave() {
return DruidDataSourceBuilder.create().build();
}
/**
* 动态数据源配置
*
* @return
*/
@Bean
@Primary
public DataSource multipleDataSource(@Qualifier("master") DataSource master, @Qualifier("slave") DataSource slave) {
DynamicDataSource multipleDataSource = new DynamicDataSource();
Map<Object, Object> targetDataSources = new HashMap<>();
targetDataSources.put(DataSourceEnum.MASTER.getValue(), master);
targetDataSources.put(DataSourceEnum.SLAVE.getValue(), slave);
//添加数据源
multipleDataSource.setTargetDataSources(targetDataSources);
//设置默认数据源
multipleDataSource.setDefaultTargetDataSource(master);
return multipleDataSource;
}
测试类如下:
实现效果如下:
至此我们的多数据源配置(主从数据源)已完成,此种方式已经过亲测验证!
可运行完整源码下载地址:http://zyshare.cn/resource/detail/10