1.application.properties配置如下:
#数据源1
spring.jdbc.datasource.ds1.driver-class-name=com.mysql.jdbc.Driver
spring.jdbc.datasource.ds1.url=jdbc:mysql://localhost:3306/test?zeroDateTimeBehavior=convertToNull&allowMultiQueries=true
spring.jdbc.datasource.ds1.username=root
spring.jdbc.datasource.ds1.password=root
spring.jdbc.datasource.ds1.type = com.alibaba.druid.pool.DruidDataSource
###########################################################
#数据源2
spring.jdbc.datasource.ds2.driver-class-name=com.mysql.jdbc.Driver
spring.jdbc.datasource.ds2.url=jdbc:mysql://localhost:3307/test?zeroDateTimeBehavior=convertToNull&allowMultiQueries=true
spring.jdbc.datasource.ds2.username=root
spring.jdbc.datasource.ds2.password=root
spring.jdbc.datasource.ds2.type = com.alibaba.druid.pool.DruidDataSource
2.加载数据源
@Configuration
@Slf4j
public class DataSourceConfig {
@Bean("springDataSource1")
@ConfigurationProperties("spring.jdbc.datasource.ds1")
public DataSource springDataSource(){
DataSource dataSource = DruidDataSourceBuilder.create().build();
return dataSource;
}
@Bean("springDataSource2")
@ConfigurationProperties("spring.jdbc.datasource.ds2")
public DataSource springDataSource(){
DataSource dataSource = DruidDataSourceBuilder.create().build();
return dataSource;
}
}
3.动态数据源配置
@Configuration
public class DynamicTransactionManager {
@Autowired
@Qualifier("springDataSource1")
private DataSource springDataSource1;
@Autowired
@Qualifier("springDataSource2")
private DataSource springDataSource2;
@Bean
public DynamicDataSource dataSource(){
//按照目标数据源名称和目标数据源对象的映射存放在Map中
Map<Object,Object> targetDataSources = new HashMap<>();
targetDataSources.put("springDataSource1",springDataSource1);
targetDataSources.put("springDataSource2",springDataSource2);
DynamicDataSource dataSource = new DynamicDataSource();
dataSource.setTargetDataSources(targetDataSources);
//设置默认的数据源,当拿不到数据源时,使用此配置
dataSource.setDefaultTargetDataSource(springDataSource1);
return dataSource;
}
@Bean
public PlatformTransactionManager txManager(DataSource dataSource){
return new DataSourceTransactionManager(dataSource);
}
}
4.动态数据源路由
public class DynamicDataSource extends AbstractRoutingDataSource {
/**
* 数据源路由,此方法用于产生要选取的数据源逻辑名称
* @return
*/
@Override
protected Object determineCurrentLookupKey() {
//从共享线程中获取数据源名称
return DynamicDataSourceHolder.getDataSource();
}
}
public class DynamicDataSourceHolder {
/**
* 本地线程共享对象
*/
private static final ThreadLocal<String> THREAD_LOCAL = new ThreadLocal<>();
public static void putDataSource(String name){
THREAD_LOCAL.set(name);
}
public static String getDataSource(){
return THREAD_LOCAL.get();
}
public static void removeDataSource(){
if(THREAD_LOCAL.get() != null)
THREAD_LOCAL.remove();
}
}
5.配置注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface TargetDataSource {
String value() default "shardingDataSource"; //此处接收的是数据源名称
}
6.配置切面
@Component
@Aspect
@Order(1)
@Slf4j
public class DataSourceAspect {
//切换放在mapper接口的方法上,所以这里要配置AOP切面的切入点
@Pointcut("@annotation(TargetDataSource)")
public void dataSourcePointCut(){
}
@Before("dataSourcePointCut()")
public void before(JoinPoint joinPoint){
Object target = joinPoint.getTarget();
String method = joinPoint.getSignature().getName();
Class<?> clazz = target.getClass();
Class<?>[] parameterTypes = ((MethodSignature)joinPoint.getSignature()).getMethod().getParameterTypes();
try{
Method m = clazz.getMethod(method,parameterTypes);
//如果方法上存在切换数据源的注解,则根据注解内容进行数据源切换
if(m!=null && m.isAnnotationPresent(TargetDataSource.class) && DynamicDataSourceHolder.getDataSource() == null){
TargetDataSource data = m.getAnnotation(TargetDataSource.class);
String dataSourceName = data.value();
DynamicDataSourceHolder.putDataSource(dataSourceName);
log.debug("current thread "+Thread.currentThread().getName()+" add data to ThreadLocal");
}else{
log.debug("switch datasource fail, use default");
}
}catch (Exception e){
log.error("current thread "+Thread.currentThread().getName()+" add data to ThreadLocal error",e);
}
}
//执行完切面后,将线程共享中的数据源名称清空
@After("dataSourcePointCut()")
public void after(JoinPoint joinPoint){
DynamicDataSourceHolder.removeDataSource();
}
}