工作中用到了动态数据源连接多个数据库的场景,记录一下;
总的实现思路是 利用spring的aop并且取消springboot的自动配置数据源,自行配置数据源的思路
1. pom.xml
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
</dependency>
2.application.properties
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
#数据库驱动
spring.datasource.local.driver-class-name=com.mysql.cj.jdbc.Driver
# 初始化连接数
spring.datasource.druid.initialSize=1
# 最大活跃连接数
spring.datasource.druid.maxActive=5
# 最小闲置连接数
spring.datasource.druid.minIdle=0
# 获取连接等待的最长时间,单位为毫秒
spring.datasource.druid.maxWait=60000
# 是否对取得的连接进行有效性检查
spring.datasource.druid.testOnBorrow=true
# 检查连接是否可用的SQL查询语句
spring.datasource.druid.validationQuery=SELECT 1
spring.datasource.druid.1.url=jdbc:mysql://***
spring.datasource.druid.1.username=***
spring.datasource.druid.1.password=***
spring.datasource.druid.2.url=jdbc:mysql://***
spring.datasource.druid.2.username=***
spring.datasource.druid.2.password=***
3. 代码
@Configuration
public class DynamicDataSourceConfig {
@Bean
@ConfigurationProperties("spring.datasource.druid.1")
public DataSource firstDataSource(){
return DruidDataSourceBuilder.create().build();
}
@Bean
@ConfigurationProperties("spring.datasource.druid.2")
public DataSource secondDataSource(){
return DruidDataSourceBuilder.create().build();
}
@Bean
@Primary
public DynamicDataSource dataSource(DataSource firstDataSource,DataSource secondDataSource){
Map<Object,Object> targetDataSources=new HashMap<>();
targetDataSources.put(DataSourceNames.FIRST,firstDataSource);
targetDataSources.put(DataSourceNames.SECOND,secondDataSource);
return new DynamicDataSource(firstDataSource,targetDataSources);
}
}
public class DynamicDataSource extends AbstractRoutingDataSource {
private static final ThreadLocal<String> CONTEXT_HOLDER=new ThreadLocal<>();
@Override
protected Object determineCurrentLookupKey() {
return getDataSource();
}
public DynamicDataSource(DataSource defaultTargetDataSource, Map<Object,Object> targetDataSource) {
super.setDefaultTargetDataSource(defaultTargetDataSource);
super.setTargetDataSources(targetDataSource);
super.afterPropertiesSet();
}
public static void setDataSource(String dataSource){
CONTEXT_HOLDER.set(dataSource);
}
public static String getDataSource(){
return CONTEXT_HOLDER.get();
}
public static void clearDataSource(){
CONTEXT_HOLDER.remove();
}
}
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DyDataSource {
String name() default "";
}
@Slf4j
@Aspect
@Component
@Order(1)
public class DataSourceAspect {
@Pointcut("@annotation(com.*.*.annotations.DyDataSource)")
public void dataSourcePoint(){
}
@Around("dataSourcePoint()")
public Object around(ProceedingJoinPoint point)throws Throwable{
MethodSignature signature = (MethodSignature)point.getSignature();
Method method = signature.getMethod();
Class<?> clazz = method.getDeclaringClass();
DyDataSource annotation = clazz.getAnnotation(DyDataSource.class);
if(annotation!=null){
DynamicDataSource.setDataSource(annotation.name());
}else{
DyDataSource methodAnnotation = method.getAnnotation(DyDataSource.class);
if(methodAnnotation!=null){
DynamicDataSource.setDataSource(methodAnnotation.name());
}else{
DynamicDataSource.setDataSource(DataSourceNames.FIRST);
}
}
try{
return point.proceed();
}finally {
DynamicDataSource.clearDataSource();
}
}
}
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
@Import({DynamicDataSourceConfig.class})
public class ManageApplication
最后在方法上或者类上加上注解就可以,自由切换数据源了,需要注意的是两个数据源是不能同时的,那个是多数据源,咱们这个是动态数据源。