springboot数据源切换

61 阅读1分钟

springboot数据源切换核心为继承 AbstractRoutingDataSource 重写 determineCurrentLookupKey

集成步骤

1.多数据源配置类
@Configuration
public class DataSourceConfig {

//    @Primary
//    @Bean("sqlSessionFactory2")
//    public SqlSessionFactory sqlSessionFactoryBean(DynamicDataSource dynamicDataSource) throws Exception {
//        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
//        sqlSessionFactoryBean.setDataSource(dynamicDataSource);
//        sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:/mapper/*.xml"));
//        org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration();
//        configuration.setMapUnderscoreToCamelCase(true);
//        configuration.setDefaultFetchSize(100);
//        configuration.setDefaultStatementTimeout(30);
//        sqlSessionFactoryBean.setConfiguration(configuration);
//        return sqlSessionFactoryBean.getObject();
//    }

    private DataSourceNewCrmProp newCrmProp;
    private DataSourceEbCrmProperties ebCrmProperties;

    @Bean("master")
    @ConditionalOnClass(DataSourceEbCrmProperties.class)
    public DataSource masterDataSource() {
        DruidDataSource druidDataSource = new DruidDataSource();
        druidDataSource.setUrl(ebCrmProperties.getUrl());
        druidDataSource.setUsername(ebCrmProperties.getUsername());
        druidDataSource.setPassword(ebCrmProperties.getPassword());
        druidDataSource.setDriverClassName(ebCrmProperties.getDriverClassName());
        return druidDataSource;
    }

    @Bean("slave")
    @ConditionalOnClass(DataSourceNewCrmProp.class)
    public DataSource slaveDataSource() {
        DruidDataSource druidDataSource = new DruidDataSource();
        druidDataSource.setUrl(newCrmProp.getUrl());
        druidDataSource.setUsername(newCrmProp.getUsername());
        druidDataSource.setPassword(newCrmProp.getPassword());
        druidDataSource.setDriverClassName(newCrmProp.getDriverClassName());
        return druidDataSource;
    }

    @Bean
    @Primary
    public DynamicDataSource dynamicDataSource() {
        Map<Object, Object> map = new HashMap<>();
        map.put(DataSourceConstants.EB_CRM, masterDataSource());
        map.put(DataSourceConstants.NEW_CRM, slaveDataSource());
        return new DynamicDataSource(masterDataSource(), map);
    }

    @Autowired
    public void setNewCrmProp(DataSourceNewCrmProp newCrmProp) {
        this.newCrmProp = newCrmProp;
    }

    @Autowired
    public void setEbCrmProperties(DataSourceEbCrmProperties ebCrmProperties) {
        this.ebCrmProperties = ebCrmProperties;
    }
}
2.集成重写AbstractRoutingDataSource
public class DynamicDataSource extends AbstractRoutingDataSource {
    @Override
    protected Object determineCurrentLookupKey() {
        return DataSourceHolder.getDataSource();
    }

    /**
     * 构造方法填充Map,构建多数据源
     */
    public DynamicDataSource(DataSource defaultTargetDataSource, Map<Object, Object> targetDataSources) {
        //默认的数据源,可以作为主数据源
        super.setDefaultTargetDataSource(defaultTargetDataSource);
        //目标数据源
        super.setTargetDataSources(targetDataSources);
        //执行afterPropertiesSet方法,完成属性的设置
        super.afterPropertiesSet();
    }

}

3.结合ThreadLocal完成切换

public class DataSourceHolder {
    //线程  本地环境
    private static final ThreadLocal<String> dataSources = new InheritableThreadLocal();

    //设置数据源
    public static void setDataSource(String datasource) {
        dataSources.set(datasource);
    }

    //获取数据源
    public static String getDataSource() {
        return dataSources.get();
    }

    //清除数据源
    public static void clearDataSource() {
        dataSources.remove();
    }
}

4.扩展 配合AOP实现自定义

@Aspect
@Component
public class DataSourceAspect {

    @Pointcut("execution(* com.ebei.syn.service.impl.yscrm.*..*(..))")
    public void dataSourcePointCut() {
    }

    @Around("dataSourcePointCut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        ChangeDataSource dsAnnotation = getDSAnnotation(joinPoint);
        if (Objects.nonNull(dsAnnotation)) {
            String dsKey = dsAnnotation.value();
            DataSourceHolder.setDataSource(dsKey);
        }
        try {
            return joinPoint.proceed();
        } finally {
            DataSourceHolder.clearDataSource();
        }
    }

    /**
     * 根据类或方法获取数据源注解
     */
    private ChangeDataSource getDSAnnotation(ProceedingJoinPoint joinPoint) {
        Class<?> targetClass = joinPoint.getTarget().getClass();
        if (targetClass.isAnnotationPresent(ChangeDataSource.class)) {
            return targetClass.getAnnotation(ChangeDataSource.class);
        }
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        if (methodSignature.getMethod().isAnnotationPresent(ChangeDataSource.class)) {
            return methodSignature.getMethod().getAnnotation(ChangeDataSource.class);
        }
        return null;
    }
}
@Documented
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(value = RetentionPolicy.RUNTIME)
public @interface ChangeDataSource {

    /**
     * 数据源名-默认为new-crm,提供给定时任务同步数据源切换
     */
    String value() default DataSourceConstants.NEW_CRM;
}