本文是根据仓库编码 和 仓库id进行按仓库进行分库处理,可以根据例子自行按照业务需要进行分库
1.核心是实现 Spring 的 AbstractRoutingDataSource 抽象类,重写 determineCurrentLookupKey 方法,实现动态数据源的目的
@Slf4j
public class DynamicDataSource extends AbstractRoutingDataSource {
/**
* ThreadLocal 线程独立存储
*/
private static final ThreadLocal<String> THREAD_HOLD_SOURCE = new InheritableThreadLocal<>();
/**
* 决定使用哪个数据源之前需要把多个数据源的信息以及默认数据源信息配置好
*
* @param defaultTargetDataSource 默认数据源
* @param targetDataSources 目标数据源
*/
public DynamicDataSource(DataSource defaultDataSource, Map<Object, Object> targetDataSources) {
super.setDefaultTargetDataSource(defaultDataSource);
super.setTargetDataSources(targetDataSources);
super.afterPropertiesSet();
}
/**
* 如果不希望数据源在启动配置时就加载好,可以定制这个方法,从任何你希望的地方读取并返回数据源
* 比如从数据库、文件、外部接口等读取数据源信息,并最终返回一个DataSource实现类对象即可
*/
@Override
protected DataSource determineTargetDataSource() {
return super.determineTargetDataSource();
}
/**
* 如果希望所有数据源在启动配置时就加载好,这里通过设置数据源Key值来切换数据,定制这个方法
*
* @return
*/
@Override
protected Object determineCurrentLookupKey() {
return getDataSource();
}
/**
* 通过同一线程获取对应
*/
public static String getDataSource() {
log.info("获取选择对应的数据源名称:{}", THREAD_HOLD_SOURCE.get());
return THREAD_HOLD_SOURCE.get();
}
public static void setDataSource(String sourceName) {
log.info("设置对应的数据源名称:{}", sourceName);
THREAD_HOLD_SOURCE.set(sourceName);
}
public static void clearDataSource() {
THREAD_HOLD_SOURCE.remove();
}
}
2.springboot项目启动类同级配置动态数据源配置
@Configuration
public class DynamicDataSourceConfig {
/**
* 数据源01 默认数据源
*/
@Bean(name = DataSourceConst.test_01)
@ConfigurationProperties("spring.datasource.test01")
public DataSource test01() {
return DruidDataSourceBuilder.create().build();
}
/**
* 数据源02
*/
@Bean(name = DataSourceConst.test_02)
@ConfigurationProperties("spring.datasource.test02")
public DataSource test02() {
return DruidDataSourceBuilder.create().build();
}
/**
* 从库数据源
*/
@Bean(name = DataSourceConst.test_01_SLAVE)
@ConfigurationProperties("spring.datasource.test01-slave")
public DataSource test01Slave() {
return DruidDataSourceBuilder.create().build();
}
/**
* 数据源02
*/
@Bean(name = DataSourceConst.test_02_SLAVE)
@ConfigurationProperties("spring.datasource.test02-slave")
public DataSource test02Slave() {
return DruidDataSourceBuilder.create().build();
}
@Bean(name = "dynamicDataSource")
@Primary
public DynamicDataSource dynamicDataSource(@Qualifier("test01") DataSource test01, @Qualifier("test02") DataSource test02,
@Qualifier("test01-slave") DataSource test01Slave, @Qualifier("test02-slave") DataSource test02Slave) {
//构建数据源集合
Map<Object, Object> dataSourceMap = new HashMap<>(5);
dataSourceMap.put(DataSourceConst.test_01, test01);
dataSourceMap.put(DataSourceConst.test_02, test02);
dataSourceMap.put(DataSourceConst.test_01_SLAVE, test01Slave);
dataSourceMap.put(DataSourceConst.test_02_SLAVE, test02Slave);
return new DynamicDataSource(test01, dataSourceMap);
}
@Bean(name = "tidbJdbcTemplate")
public JdbcTemplate tidbJdbcTemplate(@Qualifier("dynamicDataSource") DataSource dataSource) {
return new JdbcTemplate(dataSource);
}
}
3.在公共模块配置所需配置Bean
public class DataSourceConst {
public static final String test_01 = "test01";
public static final String test_02 = "test02";
public static final String test_01_SLAVE = "test01-slave";
public static final String test_02_SLAVE = "test02-slave";
/获取仓库编码字段//
public static final String WH_CODE = "whCode";
///获取动态数据源redisKey///
public static final String DATA_SOURCE_REDIS_KEY = "dynamic:data:source:";
public static final String DATA_SOURCE_PROJECT_REDIS_KEY = "dynamic:project:data:source:";
}
4.创建自定义异常类及枚举值
/**
* 异常错误code
* 重复操作
*/
@AllArgsConstructor
@Getter
public enum ErrorCodeEnum {
VALID_ERROR_MESSAGE("10001", "参数校验异常"),
ERROR_PARAMS("10002", "参数异常");
private String code;
private String message;
}
@Setter
@Getter
public class CustomException extends RuntimeException{
private String code;
private String message;
public CustomException(String message) {
this.message = message;
}
public CustomException(ErrorCodeEnum errorCodeEnum) {