Springboot动态数据源配置

1. 数据准备

create database if not exists `ds1`;
create database if not exists `ds3`;
  • ds1
/*
 Navicat Premium Data Transfer

 Source Server         : localhost_3306
 Source Server Type    : MySQL
 Source Server Version : 80030
 Source Host           : localhost:3306
 Source Schema         : ds1

 Target Server Type    : MySQL
 Target Server Version : 80030
 File Encoding         : 65001

 Date: 05/03/2024 16:45:53
*/

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for tb_user
-- ----------------------------
DROP TABLE IF EXISTS `tb_user`;
CREATE TABLE `tb_user` (
  `id` int NOT NULL AUTO_INCREMENT COMMENT 'ID',
  `username` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '用户名',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

-- ----------------------------
-- Records of tb_user
-- ----------------------------
BEGIN;
INSERT INTO `tb_user` (`id`, `username`) VALUES (1, 'wms');
COMMIT;

SET FOREIGN_KEY_CHECKS = 1;

  • ds3
/*
 Navicat Premium Data Transfer

 Source Server         : localhost_3306
 Source Server Type    : MySQL
 Source Server Version : 80030
 Source Host           : localhost:3306
 Source Schema         : ds3

 Target Server Type    : MySQL
 Target Server Version : 80030
 File Encoding         : 65001

 Date: 05/03/2024 16:45:02
*/

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for tb_user
-- ----------------------------
DROP TABLE IF EXISTS `tb_user`;
CREATE TABLE `tb_user` (
  `id` int NOT NULL AUTO_INCREMENT COMMENT 'ID',
  `username` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '用户名',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

-- ----------------------------
-- Records of tb_user
-- ----------------------------
BEGIN;
INSERT INTO `tb_user` (`id`, `username`) VALUES (1, 'zhangsan');
COMMIT;

SET FOREIGN_KEY_CHECKS = 1;

2. 版本

  • SprintBoot:2.7.11
  • Mybatis-Plus:3.5.5
  • MySQL:8.0.30

3. 引入依赖

<properties>
    <project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
    <spring-boot.version>2.7.11spring-boot.version>
    <lombok.version>1.18.30lombok.version>
    <mysql.version>8.0.33mysql.version>
    <mybatis-plus.version>3.5.5mybatis-plus.version>
properties>


<dependency>
    <groupId>org.springframework.bootgroupId>
    <artifactId>spring-boot-starter-webartifactId>
    <exclusions>
        <exclusion>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-loggingartifactId>
        exclusion>
    exclusions>
dependency>


<dependency>
    <groupId>org.springframework.bootgroupId>
    <artifactId>spring-boot-starter-log4j2artifactId>
dependency>


<dependency>
    <groupId>org.springframework.bootgroupId>
    <artifactId>spring-boot-starter-jdbcartifactId>
dependency>


<dependency>
    <groupId>org.springframework.bootgroupId>
    <artifactId>spring-boot-starter-validationartifactId>
dependency>


<dependency>
    <groupId>org.springframework.bootgroupId>
    <artifactId>spring-boot-starter-aopartifactId>
dependency>


<dependency>
    <groupId>mysqlgroupId>
    <artifactId>mysql-connector-javaartifactId>
    <scope>runtimescope>
dependency>


<dependency>
    <groupId>com.baomidougroupId>
    <artifactId>mybatis-plus-boot-starterartifactId>
dependency>


<dependency>
    <groupId>org.projectlombokgroupId>
    <artifactId>lombokartifactId>
dependency>


<dependency>
    <groupId>org.springframework.bootgroupId>
    <artifactId>spring-boot-starter-testartifactId>
    <scope>testscope>
dependency>


<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-dependenciesartifactId>
            <version>${spring-boot.version}version>
            <type>pomtype>
            <scope>importscope>
        dependency>

        
        <dependency>
            <groupId>mysqlgroupId>
            <artifactId>mysql-connector-javaartifactId>
            <version>${mysql.version}version>
        dependency>

        
        <dependency>
            <groupId>com.baomidougroupId>
            <artifactId>mybatis-plus-boot-starterartifactId>
            <version>${mybatis-plus.version}version>
        dependency>

        
        <dependency>
            <groupId>org.projectlombokgroupId>
            <artifactId>lombokartifactId>
            <version>${lombok.version}version>
        dependency>

    dependencies>
dependencyManagement>

4. 编码

  • 服务入口
/**
 * 元数据服务
 *
 * @author Wxf
 * @since 2024-03-04 09:13:15
 **/
@MapperScan(
        basePackages = "com.wxf.metadata.mapper"
)
@SpringBootApplication(
        scanBasePackages = "com.wxf.metadata"
)
public class MetadataApplication {

    public static void main(String[] args) {
        SpringApplication.run(MetadataApplication.class, args);
    }
}
  • application.yml
spring:
  profiles:
    active: dev
  • application-dev.yml
server:
  port: 8099
  shutdown: graceful

spring:
  application:
    name: metadata-service
  datasource:
    jdbcUrl: jdbc:mysql://127.0.0.1:3306/ds1?useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&transformedBitIsBoolean=true&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true&useIPv6=false
    username: root
    password: root
    driverClassName: com.mysql.cj.jdbc.Driver
    hikari:
      maximum-pool-size: 500
      max-lifetime: 18000000
      minimum-idle: 30
      connection-timeout: 30000
      connection-test-query: SELECT 1
      pool-name: HiKariDataSource
      type: com.zaxxer.hikari.HikariDataSource
      idle-timeout: 180000
      auto-commit: true
  jackson:
    date-format: yyyy-MM-dd HH:mm:ss
    time-zone: GMT+8

# Mybatis Plus 配置
mybatis-plus:
  mapper-locations: classpath:/mapper/*.xml
  type-aliases-package: com.dcits.metadata.entity
  check-config-location: false # 是否xml是否存在
  global-config:
    db-config:
      id-type: assign_id # 默认使用雪花ID
      logic-delete-field: deleted # 是否逻辑删除
      logic-delete-value: 1  # 逻辑删除:是
      logic-not-delete-value: 0 # 逻辑删除:否
  configuration:
    map-underscore-to-camel-case: true # 启用驼峰命名
    default-enum-type-handler: com.baomidou.mybatisplus.core.handlers.MybatisEnumTypeHandler
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 控制台打印日志

# 日志
logging:
  config: classpath:log4j2-spring.xml
  charset:
    file: UTF-8


  • log4j2-spring.xml



<configuration monitorInterval="5">
    
    <Properties>
        
        
        <property name="LOG_PATTERN" value="%date{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n"/>
        
        <property name="FILE_PATH" value="${env:LOG_DIR:-logs/metadata-service}"/>
        <property name="FILE_NAME" value="metadata-service"/>
    Properties>

    <appenders>

        <console name="Console" target="SYSTEM_OUT">
            
            <PatternLayout pattern="${LOG_PATTERN}"/>
            
            <ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY"/>
        console>
        
        <RollingFile name="RollingFileInfo" fileName="${FILE_PATH}/${FILE_NAME}-info.log"
                     filePattern="${FILE_PATH}/backup/${FILE_NAME}-INFO-%d{yyyy-MM-dd}_%i.log.gz">
            
            <ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY"/>
            <PatternLayout pattern="${LOG_PATTERN}"/>
            <Policies>
                
                <TimeBasedTriggeringPolicy interval="1"/>
                <SizeBasedTriggeringPolicy size="10MB"/>
            Policies>
            
            <DefaultRolloverStrategy max="15"/>
        RollingFile>

        
        <RollingFile name="RollingFileWarn" fileName="${FILE_PATH}/${FILE_NAME}-warn.log"
                     filePattern="${FILE_PATH}/backup/${FILE_NAME}-WARN-%d{yyyy-MM-dd}_%i.log.gz">
            
            <ThresholdFilter level="warn" onMatch="ACCEPT" onMismatch="DENY"/>
            <PatternLayout pattern="${LOG_PATTERN}"/>
            <Policies>
                
                <TimeBasedTriggeringPolicy interval="1"/>
                <SizeBasedTriggeringPolicy size="10MB"/>
            Policies>
            
            <DefaultRolloverStrategy max="15"/>
        RollingFile>

        
        <RollingFile name="RollingFileDebug" fileName="${FILE_PATH}/${FILE_NAME}-debug.log"
                     filePattern="${FILE_PATH}/backup/${FILE_NAME}-DEBUG-%d{yyyy-MM-dd}_%i.log.gz">
            
            <ThresholdFilter level="debug" onMatch="ACCEPT" onMismatch="DENY"/>
            <PatternLayout pattern="${LOG_PATTERN}"/>
            <Policies>
                
                <TimeBasedTriggeringPolicy interval="1"/>
                <SizeBasedTriggeringPolicy size="10MB"/>
            Policies>
            
            <DefaultRolloverStrategy max="15"/>
        RollingFile>

        
        <RollingFile name="RollingFileError" fileName="${FILE_PATH}/${FILE_NAME}-error.log"
                     filePattern="${FILE_PATH}/backup/${FILE_NAME}-ERROR-%d{yyyy-MM-dd}_%i.log.gz">
            
            <ThresholdFilter level="error" onMatch="ACCEPT" onMismatch="DENY"/>
            <PatternLayout pattern="${LOG_PATTERN}"/>
            <Policies>
                
                <TimeBasedTriggeringPolicy interval="1"/>
                <SizeBasedTriggeringPolicy size="10MB"/>
            Policies>
            
            <DefaultRolloverStrategy max="15"/>
        RollingFile>
    appenders>

    
    
    <loggers>

        <root level="DEBUG">
            <appender-ref ref="Console"/>
            <appender-ref ref="RollingFileInfo"/>
            <appender-ref ref="RollingFileWarn"/>
            <appender-ref ref="RollingFileError"/>
            <appender-ref ref="RollingFileDebug"/>
        root>
    loggers>

configuration>
  • Mybatis-Plus配置

import com.baomidou.mybatisplus.autoconfigure.MybatisPlusPropertiesCustomizer;
import com.baomidou.mybatisplus.core.MybatisConfiguration;
import com.baomidou.mybatisplus.core.config.GlobalConfig;
import com.baomidou.mybatisplus.core.handlers.MybatisEnumTypeHandler;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * Mybatis Plus 配置
 *
 * @author Wxf
 * @since 2024-01-22 10:59:28
 **/
@Configuration
public class MybatisPlusConfig {


    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
        // 设置数据库类型为mysql
        // 如果配置多个插件,切记分页最后添加
        // 如果有多数据源可以不配具体类型 否则都建议配上具体的DbType
        mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());

        // 乐观锁插件
        mybatisPlusInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());

        return mybatisPlusInterceptor;
    }

//    @Bean
    public MybatisPlusPropertiesCustomizer mybatisPlusPropertiesCustomizer() {
        return properties -> {
            GlobalConfig globalConfig = properties.getGlobalConfig();
            globalConfig.setBanner(false);
            MybatisConfiguration configuration = new MybatisConfiguration();
            // 配置枚举
            configuration.setDefaultEnumTypeHandler(MybatisEnumTypeHandler.class);
            properties.setGlobalConfig(globalConfig);
        };
    }
}
  • 动态数据源对象
/**
 * 动态数据源
 *
 * @author Wxf
 * @since 2024-03-05 10:47:30
 **/
public class DynamicDatasource extends AbstractRoutingDataSource {

    @Override
    protected Object determineCurrentLookupKey() {
        return DatasourceContextHolder.getDataSource();
    }
}

  • 缓存数据源,供动态注入

/**
 * 数据源缓存区
 *
 * @author Wxf
 * @since 2024-03-05 10:56:45
 **/
public class DatasourceMapCache {

    private static final Map<Object, Object> DATA_SOURCE_MAP = new ConcurrentHashMap<>(16);


    public static Map<Object, Object> getDataSourceMap() {
        return DATA_SOURCE_MAP;
    }

    public static void refreshDataSource(String datasourceKey, DataSource dataSource) {
        // 将新增数据源放入到Map中,作为目标数据源
        DATA_SOURCE_MAP.put(datasourceKey, dataSource);

        // 获取到动态数据源对象
        DynamicDatasource dynamicDatasource = SpringApplicationContext.getBean(DynamicDatasource.class);
        // 设置目标数据源
        dynamicDatasource.setTargetDataSources(DATA_SOURCE_MAP);
        // 刷新并输出化新增数据源
        dynamicDatasource.afterPropertiesSet();
    }

    public static void removeDataSource(String datasourceKey) {
        DATA_SOURCE_MAP.remove(datasourceKey);
    }
}

  • 数据源切换器
/**
 * 数据源holder
 *
 * @author Wxf
 * @since 2024-03-05 10:48:31
 **/
public class DatasourceContextHolder {

    private static final ThreadLocal<String> DATA_SOURCE_THREAD_LOCAL = ThreadLocal.withInitial(() -> "defaultDatasource");

    public static String getDataSource() {
        return DATA_SOURCE_THREAD_LOCAL.get();
    }

    public static void setDataSource(String datasourceKey) {
        DATA_SOURCE_THREAD_LOCAL.set(datasourceKey);
    }

    public static void remove() {
        DATA_SOURCE_THREAD_LOCAL.remove();
    }

}

  • 数据源配置

import javax.sql.DataSource;
import java.util.Map;

/**
 * 数据源配置
 *
 * @author Wxf
 * @since 2024-03-05 11:04:39
 **/
@Configuration
public class DatasourceConfig {

    @Bean
    public JdbcTemplate jdbcTemplate(@Qualifier("dynamicDatasource") DynamicDatasource dynamicDatasource) {
        return new JdbcTemplate(dynamicDatasource);
    }

    // 构建默认数据源
    @Bean(name = "defaultDatasource")
    @ConfigurationProperties(prefix = "spring.datasource")
    public DataSource dataSource() {
        return DataSourceBuilder.create().build();
    }

    // 动态数据源
    @Bean("dynamicDatasource")
    public DynamicDatasource dynamicDatasource(@Qualifier("defaultDatasource") DataSource dataSource) {
        DynamicDatasource dynamicDatasource = new DynamicDatasource();
        // 设置默认数据源
        dynamicDatasource.setDefaultTargetDataSource(dataSource);

        Map<Object, Object> dataSourceMap = DatasourceMapCache.getDataSourceMap();
        // 将默认数据源放入到目标数据源
        dataSourceMap.put("defaultDatasource", dataSource);

        // 设置目标数据源
        dynamicDatasource.setTargetDataSources(dataSourceMap);

        return dynamicDatasource;
    }

    @Bean
    public MybatisSqlSessionFactoryBean sqlSessionFactory(@Qualifier("dynamicDatasource") DynamicDatasource dynamicDatasource) throws Exception {
        MybatisSqlSessionFactoryBean sqlSessionFactoryBean = new MybatisSqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(dynamicDatasource);
        sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver()
                .getResources("classpath*:mapper/*.xml"));
        return sqlSessionFactoryBean;
    }

    @Bean
    public PlatformTransactionManager platformTransactionManager(@Qualifier("dynamicDatasource") DynamicDatasource dynamicDatasource) {
        return new DataSourceTransactionManager(dynamicDatasource);
    }
}

  • 数据源注解
/**
 * 数据源注解
 *
 * @author Wxf
 * @since 2024-01-29 09:50:26
 **/
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Ds {

    String value() default "datasource";
}

  • 注解切换
import com.dcits.metadata.config.datasource.DatasourceContextHolder;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.util.Objects;

/**
 * 数据源 Aspect
 *
 * @author Wxf
 * @since 2024-01-29 09:55:37
 **/
@Aspect
@Component
public class DynamicDatasourceAspect {

    // 切点
    @Pointcut("@annotation(com.dcits.metadata.config.datasource.aspect.Ds)")
    public void dynamicDatasource() {

    }

    // 环绕
    @Around("dynamicDatasource()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        try {
            // 类注解
            Class<?> clazz = joinPoint.getTarget().getClass();
            Ds clasDs = clazz.getAnnotation(Ds.class);

            // 方法注解
            MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
            Ds methodDs = methodSignature.getMethod().getAnnotation(Ds.class);
            if (Objects.nonNull(methodDs)) {
                // 方法上的数据源不为空则优先设置方法上的
                DatasourceContextHolder.setDataSource(methodDs.value());
            } else {
                DatasourceContextHolder.setDataSource(clasDs.value());
            }
            return joinPoint.proceed();
        } finally {
            DatasourceContextHolder.remove();
        }
    }
}

  • Spring上下文工具类

/**
 * Spring上下文工具类
 *
 * @author Wxf
 * @since 2024-01-23 19:49:39
 **/
@Component
public class SpringApplicationContext implements ApplicationContextAware {

    private static ApplicationContext applicationContext;


    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        SpringApplicationContext.applicationContext = applicationContext;
    }

    public static ApplicationContext getApplicationContext() {
        return applicationContext;
    }

    public static <T> T getBean(Class<T> clazz) {
        return applicationContext.getBean(clazz);
    }

    public static Object getBean(String name) {
        return applicationContext.getBean(name);
    }

    public static <T> T getBean(String name, Class<T> requiredType) {
        return applicationContext.getBean(name, requiredType);
    }

    // 推送Event
    public static void publishEvent(Object event) {
        applicationContext.publishEvent(event);
    }
}

  • 动态数据源工具类

import com.dcits.metadata.config.datasource.DatasourceMapCache;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;

import javax.sql.DataSource;

/**
 * 动态数据源工具类
 *
 * @author Wxf
 * @since 2024-03-05 16:11:38
 **/
public class HikariConfigUtils {

    /**
     * 初始化数据源
     *
     * @param url      URL连接地址
     * @param driver   驱动
     * @param username 用户名
     * @param password 密码
     * @return 数据源
     */
    public static DataSource initHikariDatasource(String url, String driver, String username, String password) {
        HikariConfig hikariConfig = new HikariConfig();
        hikariConfig.setJdbcUrl(url);
        hikariConfig.setDriverClassName(driver);
        hikariConfig.setUsername(username);
        hikariConfig.setPassword(password);
        return new HikariDataSource(hikariConfig);
    }


    /**
     * 刷新数据源
     *
     * @param datasourceKey 数据源Key
     * @param dataSource    数据源
     */
    public static void refreshDataSource(String datasourceKey, DataSource dataSource) {
        DatasourceMapCache.refreshDataSource(datasourceKey, dataSource);
    }
}

5. 测试



import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.Serializable;

/**
 * @author Wxf
 * @since 2024-01-27 15:03:50
 **/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@TableName("tb_user")
public class User implements Serializable {

    @TableId(value = "id", type = IdType.AUTO)
    private Integer id;

    private String username;

}

  • Mapper
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.dcits.metadata.entity.User;
import org.apache.ibatis.annotations.Mapper;

/**
 * @author Wxf
 * @since 2024-03-05 11:40:43
 **/
@Mapper
public interface UserMapper extends BaseMapper {
}
  • UserService

import com.baomidou.mybatisplus.extension.service.IService;
import com.dcits.metadata.entity.User;

import java.util.List;

/**
 * 用户Service
 *
 * @author Wxf
 * @since 2024-03-05 11:41:21
 **/
public interface UserService extends IService<User> {


    /**
     * 手动切换数据源
     *
     * @return 用户列表
     */
    List<User> selectUserList();

    /**
     * 手动切换数据源
     *
     * @return 用户列表
     */
    List<User> getDynamicUserList();
}

  • UserServiceImpl

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.dcits.metadata.config.datasource.DatasourceContextHolder;
import com.dcits.metadata.entity.User;
import com.dcits.metadata.mapper.UserMapper;
import com.dcits.metadata.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.util.List;

/**
 * 用户Service实现
 *
 * @author Wxf
 * @since 2024-03-05 11:41:57
 **/
@Slf4j
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    @Override
    public List<User> selectUserList() {
        return this.baseMapper.selectList(null);
    }

    @Override
    public List<User> getDynamicUserList() {
        DatasourceContextHolder.setDataSource("ds3");
        List<User> userList = this.baseMapper.selectList(null);
        DatasourceContextHolder.remove();
        return userList;
    }
}

  • 测试数据源

import com.dcits.metadata.service.UserService;
import com.dcits.metadata.utils.HikariConfigUtils;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

import javax.annotation.Resource;
import javax.sql.DataSource;

/**
 * 测试动态数据源
 *
 * @author Wxf
 * @since 2024-03-05 11:46:06
 **/
@SpringBootTest
public class UserTest {

    @Resource
    private UserService userService;

    @Test
    void selectUserList() {
        System.out.println(this.userService.selectUserList());
    }

    @Test
    void getDynamicUserList() {
        DataSource dataSource = HikariConfigUtils.initHikariDatasource(
                "jdbc:mysql://127.0.0.1:3306/ds3?useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&transformedBitIsBoolean=true&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true&useIPv6=false",
                "com.mysql.cj.jdbc.Driver",
                "root",
                "root"
        );

        HikariConfigUtils.refreshDataSource("ds3", dataSource);

        System.out.println(this.userService.getDynamicUserList());
    }

}

你可能感兴趣的:(spring,boot,java,后端,动态数据源,运行时修改数据源)