基于AbstractRoutingDataSource实现普通数据源和shardingSphere数据源的动态路由

108 阅读1分钟

由于工作需要,要求能在普通spring数据源和shardingSphere数据源之间的动态切换。默认走shardingSphere数据源,由shardingSphere进行读写分离、分布式事物、数据分片等管理。然后可以根据某个动态标识,走普通spring数据源。实现如下:

1、首选创建两种类型的数据源

1)普通数据源

/**
 * 非ShardingSphere托管的数据源
 * @return slaveDataSource
 */
@Bean
@ConfigurationProperties(prefix = "spring.datasource.slave")
public DataSource slaveDataSource() {
    return DataSourceBuilder.create().build();
}

2)定义shardingSphere数据源,这里是使用YamlShardingSphereDataSourceFactory工厂类,从nacos读取sharding-jdbc.yml配置,进行数据源创建

/**
 * ShardingSphere托管的数据源
 * @return shardingDataSource
 */
@Bean
public DataSource dataSource(){
    ConfigService configService = nacosConfigManager.getConfigService();
    try {
        String shardingConfig = configService.getConfig("sharding-jdbc.yml", "DEFAULT_GROUP", 1000);
        return  YamlShardingSphereDataSourceFactory.createDataSource(shardingConfig.getBytes());
    }catch (Exception e) {
        e.printStackTrace();
    }

}

3)定义动态路由数据源

    @Bean
    @Primary
    public DataSource routingDataSource(@Qualifier("dataSource") DataSource shardingDataSource,
                                        @Qualifier("slaveDataSource") DataSource slaveDataSource) {
        DynamicRoutingDataSource routingDataSource = new DynamicRoutingDataSource();
        Map<Object, Object> targetDataSources = new HashMap<>();
        targetDataSources.put("shardingDataSource",shardingDataSource);
        targetDataSources.put("slaveDataSource",slaveDataSource);
        routingDataSource.setTargetDataSources(targetDataSources);
        routingDataSource.setDefaultTargetDataSource(shardingDataSource);
        return routingDataSource;
    }

2、继承AbstractRoutingDataSource类,AbstractRoutingDataSource是Spring提供的一个抽象类,用于定义路由数据源的基本原理和实现方式,它有一个抽象的方法 determineCurrentLookupKey(),这个方法决定使用哪个数据源。

public class DynamicRoutingDataSource extends AbstractRoutingDataSource {
    @Override
    protected Object determineCurrentLookupKey() {
        return DbContextHolder.getDbType();
    }
}
package com.demo;

/**
 * 数据库动态路由上下文
 *
 * @author daily360
 */
public class DbContextHolder {
    private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();

    public static void setDbType(String dbType) {
        contextHolder.set(dbType);
    }
    public static String getDbType() {
        return contextHolder.get();
    }
    public static void clearDbType() {
        contextHolder.remove();
    }
}

3、最后在自定义注解,根据实际业务需要传入相应的路由key,在切面类拦截注解并且将路由key赋给DbContextHolder。如下

@Ds(DsType.slaveDataSource)
@Override
public Map<String, Long> rangeStatistics(RequestParam param){
    return service.rangeStatistics(param);
}