- SpringBoot多数据源配置
- 1、动态配置数据源
- 1.1、项目结构
- 1.2、配置文件
- 1.3、动态数据源配置
- 1.3.1、获取动态数据源类
- 1.3.2、动态选择数据源
- 1.3.3、配置类
- 1.3.4、如何使用
- 2、通过AOP动态获取数据源
- 2.1、项目结构
- 2.2、自定义注解
- 2.3、AOP切面
- 2.4、如何使用
- 3、客户可通过接口直接配置数据源
- 3.1、代码
1、动态配置数据源
项目地址
https://github.com/zhengguofeng1998/DynamicDataSource/tree/master/SecondDemo
1.1、项目结构 1.2、配置文件server:
port: 1816
spring:
datasource:
primary:
url: jdbc:mysql://localhost:3306/seata_account?useUnicode=true&characterEncoding=utf-8&useSSL=false
username: root
password: 123456
driver-class-name: org.gjt.mm.mysql.Driver
slave:
url: jdbc:mysql://localhost:3306/seata_order?useUnicode=true&characterEncoding=utf-8&useSSL=false
username: root
password: 123456
driver-class-name: org.gjt.mm.mysql.Driver
application:
name: dynamic-datasource
# mybatis-plus
mybatis-plus:
type-aliases-package: com.zgf.springcloud.domain
# 默认位置,可不配置
# mapper-locations: classpath*:/mapper*.xml
1.3、动态数据源配置
1.3.1、获取动态数据源类
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return DynamicDataSourceContextHolder.getContextKey();
}
}
- 继承抽象类 AbstractRoutingDataSource ,需要实现方法 determineCurrentLookupKey,即路由策略。
- 动态路由策略下一步实现,当前策略直接返回 primary数据源
public class DynamicDataSourceContextHolder {
private static final ThreadLocal DATASOURCE_CONTEXT_KEY_HOLDER = new ThreadLocal<>();
public static void setContextKey(String key) {
DATASOURCE_CONTEXT_KEY_HOLDER.set(key);
}
public static String getContextKey() {
String key = DATASOURCE_CONTEXT_KEY_HOLDER.get();
return key == null ? DataSourceConstants.DS_KEY_PRIMARY : key;
}
public static void removeContextKey() {
DATASOURCE_CONTEXT_KEY_HOLDER.remove();
}
}
- 利用ThreadLocal保存数据源,value未dataSource,线程隔离
- DynamicDataSourceContextHolder默认获取DS_KEY_PRIMARY数据源
@Configuration
@MapperScan(basePackages = "com.zgf.springcloud.mapper")
public class DynamicDataSourceConfig {
@Bean(DataSourceConstants.DS_KEY_PRIMARY)
@ConfigurationProperties(prefix = "spring.datasource.primary")
public DruidDataSource primaryDataSource() {
DruidDataSource druidDataSource = new DruidDataSource();
return dataSource(druidDataSource);
}
@Bean(DataSourceConstants.DS_KEY_SLAVE)
@ConfigurationProperties(prefix = "spring.datasource.slave")
public DruidDataSource slaveDataSource() {
DruidDataSource druidDataSource = new DruidDataSource();
return dataSource(druidDataSource);
}
public DruidDataSource dataSource(DruidDataSource druidDataSource) {
druidDataSource.setInitialSize(0);
druidDataSource.setMaxActive(180);
druidDataSource.setMaxWait(60000);
druidDataSource.setMinIdle(0);
druidDataSource.setValidationQuery("Select 1 from DUAL");
druidDataSource.setTestOnBorrow(false);
druidDataSource.setTestOnReturn(false);
druidDataSource.setTestWhileIdle(true);
druidDataSource.setTimeBetweenEvictionRunsMillis(60000);
druidDataSource.setMinEvictableIdleTimeMillis(25200000);
druidDataSource.setRemoveAbandoned(true);
druidDataSource.setRemoveAbandonedTimeout(1800);
druidDataSource.setLogAbandoned(true);
return druidDataSource;
}
@Bean
@Primary
public DataSource dynamicDataSource() {
Map
- @MapperScan扫描多数据源共同的mapper文件,统一配置,不需要单独扫描
- @ConfigurationProperties读取配置文件的数据
- 将数据当如到map中存入DynamicDataSource
@Override
public OrderDO getOrderById(Long id) {
DynamicDataSourceContextHolder.setContextKey(DataSourceConstants.DS_KEY_SLAVE);
OrderDO orderDO = orderDao.getOrderById(id);
DynamicDataSourceContextHolder.removeContextKey();
return orderDO;
}
- 先指定对应的数据源
- 运行完sql之后移除数据源
项目地址
https://github.com/zhengguofeng1998/DynamicDataSource/tree/master/ThirdDemo
2.1、项目结构 2.2、自定义注解@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface DS {
String value() default DataSourceConstants.DS_KEY_PRIMARY;
}
2.3、AOP切面
@Aspect
@Component
public class DynamicDataSourceAspect {
// @Pointcut("execution(* com.zgf.aop.Buy.buy(double)) && args(price)")
@Pointcut("@annotation(com.zgf.springcloud.annotation.DS)")
public void dataSourcePointCut() {
}
@Around("dataSourcePointCut()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
String dsKey = getDSAnnotation(joinPoint).value();
DynamicDataSourceContextHolder.setContextKey(dsKey);
try {
return joinPoint.proceed();
} finally {
DynamicDataSourceContextHolder.removeContextKey();
}
}
private DS getDSAnnotation(ProceedingJoinPoint joinPoint) {
Class> targetClass = joinPoint.getTarget().getClass();
DS dsAnnotation = targetClass.getAnnotation(DS.class);
// 先判断类的注解,再判断方法注解
if (Objects.nonNull(dsAnnotation)) {
return dsAnnotation;
} else {
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
return methodSignature.getMethod().getAnnotation(DS.class);
}
}
}
2.4、如何使用
@Override
@DS(DataSourceConstants.DS_KEY_SLAVE)
public OrderDO getOrderById(Long id) {
OrderDO orderDO = orderDao.getOrderById(id);
return orderDO;
}
- 在方法上添加@DS注解指定对应的数据源的key
@GetMapping("/addDataSource")
public String addDataSource() {
Map
- 因为DruidDataSource实现了AbstractRoutingDataSource,只需要把数据源放入到父类中,然后使用的时候再获取即可。
- 调用setTargetDataSources方法,设置新加入的数据源
- 使用和之前一样可通过注解进行配置



