小工具知识网

当前位置: 首页 > 行业知识 > SpringBoot整合Druid数据库连接池&多数据源&注解切换&动态添加

行业知识

SpringBoot整合Druid数据库连接池&多数据源&注解切换&动态添加

2026-11-12 09:02:09 来源:互联网转载

1. 引入依赖

                                        com.alibaba                druid-spring-boot-starter                1.2.16                                                    mysql                mysql-connector-java            

2. 配置数据源

spring:  datasource:    # 数据源基本配置    username: 账号    password: 密码    url: jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai    # driver-class需要注意mysql驱动的版本(com.mysql.cj.jdbc.Driver 或 com.mysql.jdbc.Driver)    driver-class-name: com.mysql.jdbc.Driver    type: com.alibaba.druid.pool.DruidDataSource    # Druid的其他属性配置    druid:      # 初始化时建立物理连接的个数      initial-size: 10      # 连接池的最小空闲数量      min-idle: 5      # 连接池最大连接数量      max-active: 20      # 获取连接时最大等待时间,单位毫秒      max-wait: 60000      # 申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。      test-while-idle: true      # 既作为检测的间隔时间又作为testWhileIdel执行的依据      time-between-eviction-runs-millis: 60000      # 销毁线程时检测当前连接的最后活动时间和当前时间差大于该值时,关闭当前连接(配置连接在池中的最小生存时间)      min-evictable-idle-time-millis: 30000      # 用来检测数据库连接是否有效的sql 必须是一个查询语句(oracle中为 select 1 from dual)      validation-query: SELECT 1 FROM DUAL      # 申请连接时会执行validationQuery检测连接是否有效,开启会降低性能,默认为true      test-on-borrow: false      # 归还连接时会执行validationQuery检测连接是否有效,开启会降低性能,默认为true      test-on-return: false      # 是否缓存preparedStatement, 也就是PSCache,PSCache对支持游标的数据库性能提升巨大,比如说oracle,在mysql下建议关闭。      pool-prepared-statements: false      # 置监控统计拦截的filters,去掉后监控界面sql无法统计,stat: 监控统计、Slf4j:日志记录、waLL: 防御sqL注入      filters: stat,wall,slf4j      # 要启用PSCache,必须配置大于0,当大于0时,poolPreparedStatements自动触发修改为true。在Druid中,不会存在Oracle下PSCache占用内存过多的问题,可以把这个数值配置大一些,比如说100      max-pool-prepared-statement-per-connection-size: -1      # 合并多个DruidDataSource的监控数据      use-global-data-source-stat: true      # 通过connectProperties属性来打开mergeSql功能;慢SQL记录      connect-properties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000      web-stat-filter:        # 是否启用StatFilter默认值true        enabled: true        # 添加过滤规则        url-pattern: /*        # 忽略过滤的格式        exclusions: /druid/*,*.js,*.gif,*.jpg,*.png,*.css,*.ico      stat-view-servlet:        # 是否启用StatViewServlet默认值true        enabled: true        # 访问路径为/druid时,跳转到StatViewServlet        url-pattern: /druid/*        # 是否能够重置数据        reset-enable: false        # 需要账号密码才能访问控制台,默认为root        login-username: root        login-password: 123456        # IP白名单        allow: 127.0.0.1        # IP黑名单(共同存在时,deny优先于allow)        deny:

配置好之后 Druid 会通过 DruidDataSourceAutoConfigure 自动装配

package com.alibaba.druid.spring.boot.autoconfigure;@Configuration@ConditionalOnClass(DruidDataSource.class)@AutoConfigureBefore(DataSourceAutoConfiguration.class)@EnableConfigurationProperties({DruidStatProperties.class, DataSourceProperties.class})@Import({DruidSpringAopConfiguration.class,        DruidStatViewServletConfiguration.class,        DruidWebStatFilterConfiguration.class,        DruidFilterConfiguration.class})public class DruidDataSourceAutoConfigure {    private static final Logger LOGGER = LoggerFactory.getLogger(DruidDataSourceAutoConfigure.class);    @Bean(initMethod = "init")    @ConditionalOnMissingBean    public DataSource dataSource() {        LOGGER.info("Init DruidDataSource");        return new DruidDataSourceWrapper();    }}

3. Druid 配置多数据源

属性配置

# 数据源配置spring:    datasource:        type: com.alibaba.druid.pool.DruidDataSource        driverClassName: com.mysql.cj.jdbc.Driver        druid:            # 主库数据源            master:                url: jdbc:mysql://localhost:3306/large_screen_monitor?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8                username: root                password: root            # 从库数据源            slave:                # 从数据源开关/默认关闭                enabled: false                url:                 username:                 password:             # 初始连接数            initialSize: 5            # 最小连接池数量            minIdle: 10            # 最大连接池数量            maxActive: 20            # 配置获取连接等待超时的时间            maxWait: 60000            # 配置连接超时时间            connectTimeout: 30000            # 配置网络超时时间            socketTimeout: 60000            # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒            timeBetweenEvictionRunsMillis: 60000            # 配置一个连接在池中最小生存的时间,单位是毫秒            minEvictableIdleTimeMillis: 300000            # 配置一个连接在池中最大生存的时间,单位是毫秒            maxEvictableIdleTimeMillis: 900000            # 配置检测连接是否有效            validationQuery: SELECT 1 FROM DUAL            testWhileIdle: true            testOnBorrow: false            testOnReturn: false            webStatFilter:                 enabled: true            statViewServlet:                enabled: true                # 设置白名单,不填则允许所有访问                allow:                url-pattern: /druid/*                # 控制台管理用户名和密码                login-username: webserver                login-password: 123456            filter:                stat:                    enabled: true                    # 慢SQL记录                    log-slow-sql: true                    slow-sql-millis: 1000                    merge-sql: true                wall:                    config:                        multi-statement-allow: true
package com.test.framework.config.properties;import org.springframework.beans.factory.annotation.Value;import org.springframework.context.annotation.Configuration;import com.alibaba.druid.pool.DruidDataSource;/** * druid 配置属性 */@Configurationpublic class DruidProperties{    @Value("${spring.datasource.druid.initialSize}")    private int initialSize;    @Value("${spring.datasource.druid.minIdle}")    private int minIdle;    @Value("${spring.datasource.druid.maxActive}")    private int maxActive;    @Value("${spring.datasource.druid.maxWait}")    private int maxWait;    @Value("${spring.datasource.druid.connectTimeout}")    private int connectTimeout;    @Value("${spring.datasource.druid.socketTimeout}")    private int socketTimeout;    @Value("${spring.datasource.druid.timeBetweenEvictionRunsMillis}")    private int timeBetweenEvictionRunsMillis;    @Value("${spring.datasource.druid.minEvictableIdleTimeMillis}")    private int minEvictableIdleTimeMillis;    @Value("${spring.datasource.druid.maxEvictableIdleTimeMillis}")    private int maxEvictableIdleTimeMillis;    @Value("${spring.datasource.druid.validationQuery}")    private String validationQuery;    @Value("${spring.datasource.druid.testWhileIdle}")    private boolean testWhileIdle;    @Value("${spring.datasource.druid.testOnBorrow}")    private boolean testOnBorrow;    @Value("${spring.datasource.druid.testOnReturn}")    private boolean testOnReturn;    public DruidDataSource dataSource(DruidDataSource datasource)    {        /** 配置初始化大小、最小、最大 */        datasource.setInitialSize(initialSize);        datasource.setMaxActive(maxActive);        datasource.setMinIdle(minIdle);        /** 配置获取连接等待超时的时间 */        datasource.setMaxWait(maxWait);                /** 配置驱动连接超时时间,检测数据库建立连接的超时时间,单位是毫秒 */        datasource.setConnectTimeout(connectTimeout);                /** 配置网络超时时间,等待数据库操作完成的网络超时时间,单位是毫秒 */        datasource.setSocketTimeout(socketTimeout);        /** 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 */        datasource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);        /** 配置一个连接在池中最小、最大生存的时间,单位是毫秒 */        datasource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);        datasource.setMaxEvictableIdleTimeMillis(maxEvictableIdleTimeMillis);        /**         * 用来检测连接是否有效的sql,要求是一个查询语句,常用select 'x'。如果validationQuery为null,testOnBorrow、testOnReturn、testWhileIdle都不会起作用。         */        datasource.setValidationQuery(validationQuery);        /** 建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。 */        datasource.setTestWhileIdle(testWhileIdle);        /** 申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。 */        datasource.setTestOnBorrow(testOnBorrow);        /** 归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。 */        datasource.setTestOnReturn(testOnReturn);        return datasource;    }}

数据源枚举

package com.test.common.enums;/** * 数据源 */public enum DataSourceType{    /**     * 主库     */    MASTER,    /**     * 从库     */    SLAVE}

动态数据源

继承 AbstractRoutingDataSource 就可以实现动态数据源了

实现了一个动态数据源类的构造方法,主要是为了设置默认数据源,以及以Map保存的各种目标数据源。其中Map的key是设置的数据源名称,value则是对应的数据源(DataSource)。

在使用 DynamicDataSource 时,会调用determineCurrentLookupKey 方法拿到具体数据源的key,然后使用对应的数据源;如果没有对应的数据源,会使用 DynamicDataSource 设置的默认数据源

package com.test.framework.datasource;import java.util.Map;import javax.sql.DataSource;import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;/** * 动态数据源 */public class DynamicDataSource extends AbstractRoutingDataSource{    public DynamicDataSource(DataSource defaultTargetDataSource, Map targetDataSources)    {        super.setDefaultTargetDataSource(defaultTargetDataSource);        super.setTargetDataSources(targetDataSources);        super.afterPropertiesSet();    }    @Override    protected Object determineCurrentLookupKey()    {        return DynamicDataSourceContextHolder.getDataSourceType();    }}

数据源切换

package com.test.framework.datasource;import org.slf4j.Logger;import org.slf4j.LoggerFactory;/** * 数据源切换处理 */public class DynamicDataSourceContextHolder{    public static final Logger log = LoggerFactory.getLogger(DynamicDataSourceContextHolder.class);    /**     * 使用ThreadLocal维护变量,ThreadLocal为每个使用该变量的线程提供独立的变量副本,     *  所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。     */    private static final ThreadLocal CONTEXT_HOLDER = new ThreadLocal();    /**     * 设置数据源的变量     */    public static void setDataSourceType(String dsType)    {        log.info("切换到{}数据源", dsType);        CONTEXT_HOLDER.set(dsType);    }    /**     * 获得数据源的变量     */    public static String getDataSourceType()    {        return CONTEXT_HOLDER.get();    }    /**     * 清空数据源变量     */    public static void clearDataSourceType()    {        CONTEXT_HOLDER.remove();    }}

 数据源配置类

通过配置类,将配置文件中的配置的数据库信息转换成datasource,并添加到DynamicDataSource中,同时通过@Bean将DynamicDataSource注入Spring中进行管理,后期在进行动态数据源添加时,会用到。

package com.test.framework.config;import java.io.IOException;import java.util.HashMap;import java.util.Map;import javax.servlet.Filter;import javax.servlet.FilterChain;import javax.servlet.ServletException;import javax.servlet.ServletRequest;import javax.servlet.ServletResponse;import javax.sql.DataSource;import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;import org.springframework.boot.context.properties.ConfigurationProperties;import org.springframework.boot.web.servlet.FilterRegistrationBean;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.context.annotation.Primary;import com.alibaba.druid.pool.DruidDataSource;import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;import com.alibaba.druid.spring.boot.autoconfigure.properties.DruidStatProperties;import com.alibaba.druid.util.Utils;import com.test.common.enums.DataSourceType;import com.test.common.utils.spring.SpringUtils;import com.test.framework.config.properties.DruidProperties;import com.test.framework.datasource.DynamicDataSource;/** * druid 配置多数据源 */@Configurationpublic class DruidConfig{    @Bean    @ConfigurationProperties("spring.datasource.druid.master")    public DataSource masterDataSource(DruidProperties druidProperties)    {        DruidDataSource dataSource = DruidDataSourceBuilder.create().build();        return druidProperties.dataSource(dataSource);    }    @Bean    @ConfigurationProperties("spring.datasource.druid.slave")    @ConditionalOnProperty(prefix = "spring.datasource.druid.slave", name = "enabled", havingValue = "true")    public DataSource slaveDataSource(DruidProperties druidProperties)    {        DruidDataSource dataSource = DruidDataSourceBuilder.create().build();        return druidProperties.dataSource(dataSource);    }    @Bean(name = "dynamicDataSource")    @Primary    public DynamicDataSource dataSource(DataSource masterDataSource)    {        Map targetDataSources = new HashMap();        targetDataSources.put(DataSourceType.MASTER.name(), masterDataSource);        setDataSource(targetDataSources, DataSourceType.SLAVE.name(), "slaveDataSource");        return new DynamicDataSource(masterDataSource, targetDataSources);    }        /**     * 设置数据源     *      * @param targetDataSources 备选数据源集合     * @param sourceName 数据源名称     * @param beanName bean名称     */    public void setDataSource(Map targetDataSources, String sourceName, String beanName)    {        try        {            DataSource dataSource = SpringUtils.getBean(beanName);            targetDataSources.put(sourceName, dataSource);        }        catch (Exception e)        {        }    }    /**     * 去除监控页面底部的广告     */    @SuppressWarnings({ "rawtypes", "unchecked" })    @Bean    @ConditionalOnProperty(name = "spring.datasource.druid.statViewServlet.enabled", havingValue = "true")    public FilterRegistrationBean removeDruidFilterRegistrationBean(DruidStatProperties properties)    {        // 获取web监控页面的参数        DruidStatProperties.StatViewServlet config = properties.getStatViewServlet();        // 提取common.js的配置路径        String pattern = config.getUrlPattern() != null ? config.getUrlPattern() : "/druid/*";        String commonJsPattern = pattern.replaceAll("\\*", "js/common.js");        final String filePath = "support/http/resources/js/common.js";        // 创建filter进行过滤        Filter filter = new Filter()        {            @Override            public void init(javax.servlet.FilterConfig filterConfig) throws ServletException            {            }            @Override            public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)                    throws IOException, ServletException            {                chain.doFilter(request, response);                // 重置缓冲区,响应头不会被重置                response.resetBuffer();                // 获取common.js                String text = Utils.readFromResource(filePath);                // 正则替换banner, 除去底部的广告信息                text = text.replaceAll("
", ""); text = text.replaceAll("powered.*?shrek.wang", ""); response.getWriter().write(text); } @Override public void destroy() { } }; FilterRegistrationBean registrationBean = new FilterRegistrationBean(); registrationBean.setFilter(filter); registrationBean.addUrlPatterns(commonJsPattern); return registrationBean; }}

SpringBoot Bean工具类

package com.test.common.utils.spring;import org.springframework.aop.framework.AopContext;import org.springframework.beans.BeansException;import org.springframework.beans.factory.NoSuchBeanDefinitionException;import org.springframework.beans.factory.config.BeanFactoryPostProcessor;import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;import org.springframework.context.ApplicationContext;import org.springframework.context.ApplicationContextAware;import org.springframework.stereotype.Component;/** * spring工具类 方便在非spring管理环境中获取bean *  * @author webserver */@Componentpublic final class SpringUtils implements BeanFactoryPostProcessor, ApplicationContextAware {    /** Spring应用上下文环境 */    private static ConfigurableListableBeanFactory beanFactory;    private static ApplicationContext applicationContext;    @Override    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException     {        SpringUtils.beanFactory = beanFactory;    }    @Override    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException     {        SpringUtils.applicationContext = applicationContext;    }    /**     * 获取对象     *     * @param name     * @return Object 一个以所给名字注册的bean的实例     * @throws org.springframework.beans.BeansException     *     */    @SuppressWarnings("unchecked")    public static  T getBean(String name) throws BeansException    {        return (T) beanFactory.getBean(name);    }    /**     * 获取类型为requiredType的对象     *     * @param clz     * @return     * @throws org.springframework.beans.BeansException     *     */    public static  T getBean(Class clz) throws BeansException    {        T result = (T) beanFactory.getBean(clz);        return result;    }    /**     * 如果BeanFactory包含一个与所给名称匹配的bean定义,则返回true     *     * @param name     * @return boolean     */    public static boolean containsBean(String name)    {        return beanFactory.containsBean(name);    }    /**     * 判断以给定名字注册的bean定义是一个singleton还是一个prototype。 如果与给定名字相应的bean定义没有被找到,将会抛出一个异常(NoSuchBeanDefinitionException)     *     * @param name     * @return boolean     * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException     *     */    public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException    {        return beanFactory.isSingleton(name);    }    /**     * @param name     * @return Class 注册对象的类型     * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException     *     */    public static Class getType(String name) throws NoSuchBeanDefinitionException    {        return beanFactory.getType(name);    }    /**     * 如果给定的bean名字在bean定义中有别名,则返回这些别名     *     * @param name     * @return     * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException     *     */    public static String[] getAliases(String name) throws NoSuchBeanDefinitionException    {        return beanFactory.getAliases(name);    }    /**     * 获取aop代理对象     *      * @param invoker     * @return     */    @SuppressWarnings("unchecked")    public static  T getAopProxy(T invoker)    {        return (T) AopContext.currentProxy();    }    /**     * 获取当前的环境配置,无配置返回null     *     * @return 当前的环境配置     */    public static String[] getActiveProfiles()    {        return applicationContext.getEnvironment().getActiveProfiles();    }    /**     * 获取当前的环境配置,当有多个环境配置时,只获取第一个     *     * @return 当前的环境配置     */    public static String getActiveProfile()    {        final String[] activeProfiles = getActiveProfiles();        return (activeProfiles != null && activeProfiles.lenth > 0) ? activeProfiles[0] : null;    }    /**     * 获取配置文件中的值     *     * @param key 配置文件的key     * @return 当前的配置文件的值     *     */    public static String getRequiredProperty(String key)    {        return applicationContext.getEnvironment().getRequiredProperty(key);    }}

手动切换数据源

在需要切换数据源的方法中使用DynamicDataSourceContextHolder类实现手动切换

public List selectUserList(SysUser user){DynamicDataSourceContextHolder.setDataSourceType(DataSourceType.SLAVE.name());List userList = userMapper.selectUserList(user);DynamicDataSourceContextHolder.clearDataSourceType();return userList;}

4.实现注解切换数据源

自定义注解

package com.test.common.annotation;import java.lang.annotation.Documented;import java.lang.annotation.ElementType;import java.lang.annotation.Inherited;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;import com.test.common.enums.DataSourceType;/** * 自定义多数据源切换注解 * * 优先级:先方法,后类,如果方法覆盖了类上的数据源类型,以方法的为准,否则以类上的为准 * * @author webserver */@Target({ ElementType.METHOD, ElementType.TYPE })@Retention(RetentionPolicy.RUNTIME)@Documented@Inheritedpublic @interface DataSource{    /**     * 切换数据源名称     */    public DataSourceType value() default DataSourceType.MASTER;}

根据注解实现aop切点 

代码使用了@Around,通过ProceedingJoinPoint获取注解信息,拿到注解传递值,然后设置当前线程的数据源。

import java.util.Objects;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.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.core.annotation.AnnotationUtils;import org.springframework.core.annotation.Order;import org.springframework.stereotype.Component;import com.test.common.annotation.DataSource;import com.test.framework.datasource.DynamicDataSourceContextHolder;/** * 多数据源处理 *  * @author webserver */@Aspect@Order(1)@Componentpublic class DataSourceAspect{    protected Logger logger = LoggerFactory.getLogger(getClass());    @Pointcut("@annotation(com.example.common.annotation.DataSource)"            + "|| @within(com.example.common.annotation.DataSource)")    public void dsPointCut()    {    }    @Around("dsPointCut()")    public Object around(ProceedingJoinPoint point) throws Throwable    {        DataSource dataSource = getDataSource(point);        if (dataSource != null)        {            DynamicDataSourceContextHolder.setDataSourceType(dataSource.value().name());        }        try        {            return point.proceed();        }        finally        {            // 销毁数据源 在执行方法之后            DynamicDataSourceContextHolder.clearDataSourceType();        }    }    /**     * 获取需要切换的数据源     */    public DataSource getDataSource(ProceedingJoinPoint point)    {        MethodSignature signature = (MethodSignature) point.getSignature();        DataSource dataSource = AnnotationUtils.findAnnotation(signature.getMethod(), DataSource.class);        if (Objects.nonNull(dataSource))        {            return dataSource;        }        return AnnotationUtils.findAnnotation(signature.getDeclaringType(), DataSource.class);    }}

使用注解切换数据源

在需要使用多数据源方法或类上添加@DataSource注解,其中value用来表示数据源

@DataSource(value = DataSourceType.SLAVE)public List selectUserList(SysUser user){return userMapper.selectUserList(user);}

5. 动态添加数据源

数据源实体类

@Data@Accessors(chain = true)public class DataSourceEntity {    /**     * 数据库地址     */    private String url;    /**     * 数据库用户名     */    private String userName;    /**     * 密码     */    private String passWord;    /**     * 数据库驱动     */    private String driverClassName;    /**     * 数据库key,即保存DynamicDataSource中Map的key     */    private String key;}

修改DynamicDataSource

@Slf4jpublic class DynamicDataSource extends AbstractRoutingDataSource {    private final Map targetDataSourceMap;    public DynamicDataSource(DataSource defaultDataSource,Map targetDataSources){        super.setDefaultTargetDataSource(defaultDataSource);        super.setTargetDataSources(targetDataSources);        this.targetDataSourceMap = targetDataSources;    }    @Override    protected Object determineCurrentLookupKey() {        return DataSourceContextHolder.getDataSource();    }    /**     * 添加数据源信息     * @param dataSources 数据源实体集合     * @return 返回添加结果     */    public void createDataSource(List dataSources){        try {            if (CollectionUtils.isNotEmpty(dataSources)){                for (DataSourceEntity ds : dataSources) {                    //校验数据库是否可以连接                    Class.forName(ds.getDriverClassName());                    DriverManager.getConnection(ds.getUrl(),ds.getUserName(),ds.getPassWord());                    //定义数据源                    DruidDataSource dataSource = new DruidDataSource();                    BeanUtils.copyProperties(ds,dataSource);                    //申请连接时执行validationQuery检测连接是否有效,这里建议配置为TRUE,防止取到的连接不可用                    dataSource.setTestOnBorrow(true);                    //建议配置为true,不影响性能,并且保证安全性。                    //申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。                    dataSource.setTestWhileIdle(true);                    //用来检测连接是否有效的sql,要求是一个查询语句。                    dataSource.setValidationQuery("select 1 ");                    dataSource.init();                    this.targetDataSourceMap.put(ds.getKey(),dataSource);                }                super.setTargetDataSources(this.targetDataSourceMap);                // 将TargetDataSources中的连接信息放入resolvedDataSources管理                super.afterPropertiesSet();                return Boolean.TRUE;            }        }catch (ClassNotFoundException | SQLException e) {            log.error("---程序报错---:{}", e.getMessage());        }        return Boolean.FALSE;    }    /**     * 校验数据源是否存在     * @param key 数据源保存的key     * @return 返回结果,true:存在,false:不存在     */    public boolean existsDataSource(String key){        return Objects.nonNull(this.targetDataSourceMap.get(key));    }}

存储数据源信息的表

create table test_db_info(    id int auto_increment primary key not null comment '主键Id',    url varchar(255) not null comment '数据库URL',    username varchar(255) not null comment '用户名',    password varchar(255) not null comment '密码',    driver_class_name varchar(255) not null comment '数据库驱动'    name varchar(255) not null comment '数据库名称')insert into test_db_info(url, username, password,driver_class_name, name)value ('jdbc:mysql://xxxxx:3306/test2?characterEncoding=utf-8&allowMultiQueries=true&zeroDateTimeBehavior=convertToNull&useSSL=false',       'root','123456','com.mysql.cj.jdbc.Driver','add_slave')

SpringBoot启动时加载数据源信息

@Componentpublic class LoadDataSourceRunner implements CommandLineRunner {    @Resource    private DynamicDataSource dynamicDataSource;    @Resource    private TestDbInfoMapper testDbInfoMapper;    @Override    public void run(String... args) throws Exception {        List testDbInfos = testDbInfoMapper.selectList(null);        if (CollectionUtils.isNotEmpty(testDbInfos)) {            List ds = new ArrayList();            for (TestDbInfo testDbInfo : testDbInfos) {                DataSourceEntity sourceEntity = new DataSourceEntity();                BeanUtils.copyProperties(testDbInfo,sourceEntity);                sourceEntity.setKey(testDbInfo.getName());                ds.add(sourceEntity);            }            dynamicDataSource.createDataSource(ds);        }    }}

上一篇:【MySQL数据库原理】在MySQL Workbench界面运行SQL代码——学生管理系统

下一篇:揭秘,轻松删除JS插件添加元素的步骤指南!