- 配置文件配置数据源连接信息
## 默认数据库配置
spring.datasource.type = com.alibaba.druid.pool.DruidDataSource
spring.datasource.url =
spring.datasource.username =
spring.datasource.password =
spring.datasource.driver-class-name = com.mysql.cj.jdbc.Driver
#数据源2配置
spring.datasource.datasource2.url =
spring.datasource.datasource2.username =
spring.datasource.datasource2.password =
- 数据源标志,枚举类
/*
数据源标志,枚举类
*/
@Getter
public enum DataSourceType {
DEFAULT_DS,DATASOURCE2
}
- 项目启动注入两个数据源
import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import javax.sql.DataSource;
import java.util.HashMap;
/*
多数据源配置
*/
@Configuration
public class DataSourceConfig {
@Value("${spring.datasource.datasource2.url}")
private String datasource2Url;
@Value("${spring.datasource.datasource2.username}")
private String datasource2Username;
@Value("${spring.datasource.datasource2.password}")
private String datasource2Password;
@Value("${spring.datasource.url}")
private String defaultUrl;
@Value("${spring.datasource.username}")
private String defaultUsername;
@Value("${spring.datasource.password}")
private String defaultPassword;
@Bean("defaultSource")
public DruidDataSource defaultDataSource() {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setUrl(defaultUrl);
dataSource.setUsername(defaultUsername);
dataSource.setPassword(defaultPassword);
return dataSource;
}
@Bean("dataSource2")
public DruidDataSource liveDataSource() {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setUrl(datasource2Url);
dataSource.setUsername(datasource2Username);
dataSource.setPassword(datasource2Password);
return dataSource;
}
@Bean("data")
@Primary
public DataSource dataSource(@Qualifier("defaultSource") DataSource defaultSource, @Qualifier("dataSource2") DataSource dataSource2) {
HashMap<Object, Object> targetDataSource = new HashMap<>();
targetDataSource.put(DataSourceType.DEFAULT_DS, defaultSource);
targetDataSource.put(DataSourceType.DATASOURCE2, dataSource2);
RoutingDataSource routingDataSource = new RoutingDataSource();
routingDataSource.setTargetDataSources(targetDataSource);
routingDataSource.setDefaultTargetDataSource(defaultSource);
return routingDataSource;
}
}
- 防止多数据源线程竞争的ThreadLocal类
/*
防止多数据源线程竞争的ThreadLocal类
*/
public class DataSourceContextHolder {
private static final ThreadLocal<DataSourceType> CONTEXT_HOLDER = new ThreadLocal<>();
public static void setDataSouceType(DataSourceType dataSouceType){
CONTEXT_HOLDER.set(dataSouceType);
}
public static DataSourceType getDataSourceType(){
return CONTEXT_HOLDER.get();
}
public static void clearDataSourceType(){
CONTEXT_HOLDER.remove();
}
}
- 动态数据源:获取当前线程的DatabaseType
/*
动态数据源:获取当前线程的DatabaseType
*/
@Slf4j
public class RoutingDataSource extends AbstractRoutingDataSource {
@Override
protected DataSourceType determineCurrentLookupKey() {
DataSourceType dataSourceType = DataSourceContextHolder.getDataSourceType();
if (dataSourceType != null) {
log.info("Use database {}", dataSourceType);
}
return dataSourceType;
}
}
- 切换数据源
DataSourceContextHolder.setDataSouceType(dataSourceType);
DataSourceContextHolder.clearDataSourceType
- 用aop切换数据源
7.1 定义使用哪个数据源的注解
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/*
在方法或者类上使用,用于指定使用哪个数据源
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD,ElementType.TYPE})
public @interface UseDataSource {
DataSourceType value() default DataSourceType.DEFAULT_DS;
}
7.2 切换数据源切面类
哪个方法需要切换数据源,在方法上直接加该注解即可,value值为数据源标志枚举
import lombok.extern.slf4j.Slf4j;
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.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
/*
切换数据源切面类
*/
@Aspect
@Component
@Slf4j
@Order(-10)
public class DynamicDataSourceAspect2 {
@Before("@annotation(targetDatasource)")
public void changeDataSource(JoinPoint point, UseDataSource targetDatasource) {
DataSourceType dataSourceType = targetDatasource.value();
DataSourceContextHolder.setDataSouceType(dataSourceType);
log.info("Use DataSource : {} > {}", targetDatasource.value(), point.getSignature());
}
@After("@annotation(targetDatasource)")
public void restoreDataSource(JoinPoint point, UseDataSource targetDatasource) {
log.info("Revert DataSource : {} > {}", targetDatasource.value(), point.getSignature());
DataSourceContextHolder.clearDataSourceType();
}
}
- 注意事项!!!
如果要在一个方法中使用不同的数据源,那么这个方法上不能加事务注解,否则会切换失败。