1 Star 1 Fork 1

feizhaiyou / data-source-demo

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
该仓库未声明开源许可证文件(LICENSE),使用请关注具体项目描述及其代码上游依赖。
克隆/下载
贡献代码
同步代码
取消
提示: 由于 Git 不支持空文件夾,创建文件夹后会生成空的 .keep 文件
Loading...
README

应用场景

单个项目中需要连接多个数据源(同域同类型不同库,同域不同类型,不同域...)

核心技术

AOP:通过类、方法切入,在执行前获取所需要的数据源。

AbstractRoutingDataSource:JDBC提供,用来配置多个数据源,可以通过指定key获取配置好的数据源。【DataSource抽象实现类】

数据源配置类:配置多个数据源的bean,存放到IOC容器中,并将所有数据源bean设置到AbstractRoutingDataSource的动态数据源中。

SpringBoot启动类:排除数据源自动装配DataSourceAutoConfiguration。

配置解析

1.Maven依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.8.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <groupId>com.sen</groupId>
    <artifactId>data-source-demo</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--mybatis-->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.1</version>
        </dependency>
        <!--JDBC-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <!--MySQL驱动-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <!--<version>5.1.6</version>-->
            <version>8.0.21</version>
        </dependency>
        <!--Oracle驱动-->
        <dependency>
            <groupId>com.oracle</groupId>
            <artifactId>ojdbc8</artifactId>
            <version>18.3</version>
        </dependency>
        <!--AOP-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.1</version>
        </dependency>
        <!--druid-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.14</version>
        </dependency>
    </dependencies>
</project>

2.yml配置文件

application.yml

logging:
  level:
    com.feizhaiyou.demo: debug
mybatis:
  mapper-locations: mapper/*.xml
server:
  port: 10010
spring:
  profiles:
    active: druid

application-druid.yml

# 数据源配置
spring:
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    druid:
      master:
        # mysql8.0驱动配置
        driverClassName: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://127.0.0.1:3306/test?characterEncoding=utf8&serverTimezone=Asia/Shanghai
        username: root
        password: 123456
      # 从库数据源
      slave:
        # 是否开启从数据源(如果不是true会使用默认数据源MASTER)
        enabled: true
        driverClassName: oracle.jdbc.driver.OracleDriver(驱动类)
        url: 数据库URL(公司的Orcale数据库不便展示)
        username: 自己的数据库连接名
        password: 自己的数据库密码
      slave2:
        enabled: true
        driverClassName: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://127.0.0.1:3306/nacos?characterEncoding=utf8&serverTimezone=Asia/Shanghai
        username: root
        password: 123456
      # 初始连接数
      initialSize: 5
      # 最小连接池数量
      minIdle: 10
      # 最大连接池数量
      maxActive: 20
      # 配置获取连接等待超时的时间
      maxWait: 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: 123
        login-password: 123
      filter:
        stat:
          enabled: true
          # 慢SQL记录
          log-slow-sql: true
          slow-sql-millis: 1000
          merge-sql: true
        wall:
          config:
            multi-statement-allow: true

说明:为了方便,单独拉出的yml文件配置数据源

3.自定义注解及多数据源Key

注解用来作为AOP的切点

package com.feizhaiyou.demo.anno;

import com.feizhaiyou.demo.enums.DataSourceType;

import java.lang.annotation.*;

/**
 * 自定义多数据源切换注解
 *
 * @author Jason
 */
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface DataSource {
    /**
     * 切换数据源key 默认master
     */
    public DataSourceType value() default DataSourceType.MASTER;
}

枚举类用来作为数据源的key

package com.feizhaiyou.demo.enums;

/**
 * 数据源key
 *
 * @author Jason
 */
public enum DataSourceType {
    /**
     * 主库
     */
    MASTER,

    /**
     * 从库
     */
    SLAVE,

    /**
     * 从库2
     */
    SLAVE2,
}

4.设置AOP切面

用来获取指定的数据源配置

package com.feizhaiyou.demo.aop;


import com.feizhaiyou.demo.anno.DataSource;
import com.feizhaiyou.demo.holder.DynamicDataSourceContextHolder;
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.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

/**
 * 多数据源处理
 *
 * @author Jason
 */
@Aspect
@Order(1)
@Component
public class DataSourceAspect {
    // 切点
    @Pointcut("@annotation(com.feizhaiyou.demo.anno.DataSource)"
            + "|| @within(com.feizhaiyou.demo.anno.DataSource)")
    public void dsPointCut() {

    }

    @Around("dsPointCut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        // 获取该切点的DataSource注解
        DataSource dataSource = getDataSource(point);

        if (dataSource != null) {
            // 设置数据源key 通过该key获取DynamicDataSource中指定的动态数据源
            DynamicDataSourceContextHolder.setDataSourceType(dataSource.value().name());
        }

        try {
            return point.proceed();
        } finally {
            // 销毁数据源 在执行方法之后 清空数据源key 释放该数据源
            DynamicDataSourceContextHolder.clearDataSourceType();
        }
    }

    /**
     * 获取切点上的DataSource注解{@link DataSource}
     * @param point 切点
     * @return {@link DataSource}
     */
    private DataSource getDataSource(ProceedingJoinPoint point) {
        MethodSignature signature = (MethodSignature) point.getSignature();
        Class<? extends Object> targetClass = point.getTarget().getClass();
        DataSource targetDataSource = targetClass.getAnnotation(DataSource.class);
        if (targetDataSource != null) {
            return targetDataSource;
        } else {
            Method method = signature.getMethod();
            DataSource dataSource = method.getAnnotation(DataSource.class);
            return dataSource;
        }
    }
}

5.设置线程使用的数据源key

该线程会使用存储的key来调用对应的动态数据源

package com.feizhaiyou.demo.holder;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 数据源key处理(存储执行线程所需要的数据源的key)
 *
 * @author Jason
 */
public class DynamicDataSourceContextHolder {
    private static final Logger log = LoggerFactory.getLogger(DynamicDataSourceContextHolder.class);

    /**
     * 使用ThreadLocal维护变量,ThreadLocal为每个使用该变量的线程提供独立的变量副本,
     * 所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。
     */
    private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>();

    /**
     * 设置线程数据源key
     */
    public static void setDataSourceType(String dsType) {
        log.info("切换到{}数据源", dsType);
        CONTEXT_HOLDER.set(dsType);
    }

    /**
     * 获得线程数据源key
     */
    public static String getDataSourceType() {
        return CONTEXT_HOLDER.get();
    }

    /**
     * 清空线程数据源key
     */
    public static void clearDataSourceType() {
        CONTEXT_HOLDER.remove();
    }
}

6.动态数据源配置【重点】

实现AbstractRoutingDataSource类的方法,指定默认数据源和动态数据源集合

package com.feizhaiyou.demo.config;

import com.feizhaiyou.demo.holder.DynamicDataSourceContextHolder;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

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

/**
 * 动态数据源,配置好后可以通过key获取数据源
 *
 * @author Jason
 */
public class DynamicDataSource extends AbstractRoutingDataSource {
    /**
     * 如果不希望数据源在启动配置时就加载好,可以定制这个方法,从任何你希望的地方读取并返回数据源
     * 比如从数据库、文件、外部接口等读取数据源信息,并最终返回一个DataSource实现类对象即可
     * @return DataSource
     */
    @Override
    protected DataSource determineTargetDataSource() {
        return super.determineTargetDataSource();
    }

    /**
     * 设置需要获取的数据源的key,通过该key获取数据源,该方法直接返回需要获取的数据源的key即可
     * targetDataSources找不到该key,则会使用默认数据源
     * @return
     */
    @Override
    protected Object determineCurrentLookupKey() {
        return DynamicDataSourceContextHolder.getDataSourceType();
    }

    /**
     * 设置动态数据源,设置后可以通过determineCurrentLookupKey方法返回的key动态获取targetDataSources中的数据源
     * @param defaultTargetDataSource 默认数据源
     * @param targetDataSources 动态数据源集合:key={@link com.feizhaiyou.demo.enums.DataSourceType} value={@link DataSource}
     */
    public DynamicDataSource(DataSource defaultTargetDataSource, Map<Object, Object> targetDataSources) {
        // 设置默认数据源(MASTER)
        super.setDefaultTargetDataSource(defaultTargetDataSource);
        // 设置指定数据源(MASTER SLAVE SLAVE2)
        super.setTargetDataSources(targetDataSources);
        super.afterPropertiesSet();
    }
}

7.数据源配置类与Druid属性文件【重点】

用于手动配置数据源bean,加载到IOC容器中,并配置成动态数据源集合。

由于使用了Druid连接池,所以需要添加个属性文件来读取yml文件中的配置。

属性文件:

package com.feizhaiyou.demo.properties;

import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;

/**
 * druid 配置属性
 *
 * @author Jason
 */
@Configuration
public 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.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.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.feizhaiyou.demo.config;

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.feizhaiyou.demo.enums.DataSourceType;
import com.feizhaiyou.demo.properties.DruidProperties;
import com.feizhaiyou.demo.ustils.SpringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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 javax.servlet.*;
import javax.sql.DataSource;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

/**
 * druid 配置多数据源
 *
 * @author Jason
 */
@Configuration
public class DruidConfig {

    private static final Logger log = LoggerFactory.getLogger(DruidConfig.class);

    // 设置数据源bean
    @Bean
    @ConfigurationProperties("spring.datasource.druid.master")
    public DataSource masterDataSource(DruidProperties druidProperties) {
        DruidDataSource dataSource = DruidDataSourceBuilder.create().build();
        return druidProperties.dataSource(dataSource);
    }

    // 设置数据源bean
    @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);
    }

    // TODO 设置数据源bean 读取配置
    // 设置数据源beanName
    @Bean("slave2DataSource")
    // 读取的数据源配置
    @ConfigurationProperties("spring.datasource.druid.slave2")
    // 判断是否读取 enabled: true 读取
    @ConditionalOnProperty(prefix = "spring.datasource.druid.slave2", name = "enabled", havingValue = "true")
    public DataSource slave2DataSource(DruidProperties druidProperties) {
        DruidDataSource dataSource = DruidDataSourceBuilder.create().build();
        return druidProperties.dataSource(dataSource);
    }


    /**
     * 设置指定的动态数据源 {@link DynamicDataSource}
     *
     * @param masterDataSource
     * @return
     */
    @Bean(name = "dynamicDataSource")
    @Primary
    public DynamicDataSource dataSource(DataSource masterDataSource) {
        Map<Object, Object> targetDataSources = new HashMap<>();
        // 添加MUSTER动态数据源
        targetDataSources.put(DataSourceType.MASTER.name(), masterDataSource);
        // 添加SLAVE动态数据源
        setDataSource(targetDataSources, DataSourceType.SLAVE.name(), "slaveDataSource");
        // TODO 设置数据源 参数:动态数据源集合,动态数据源key,对应数据源beanName
        setDataSource(targetDataSources, DataSourceType.SLAVE2.name(), "slave2DataSource");
        // 设置默认数据源和指定的动态数据源
        return new DynamicDataSource(masterDataSource, targetDataSources);
    }

    /**
     * 设置数据源
     *
     * @param targetDataSources 动态数据源集合
     * @param sourceName        动态数据源key
     * @param beanName          bean名称
     */
    private void setDataSource(Map<Object, Object> targetDataSources, String sourceName, String beanName) {
        try {
            // 通过bean名称从spring IOC中获取数据源bean
            DataSource dataSource = SpringUtils.getBean(beanName);
            // 添加到动态数据源集合
            targetDataSources.put(sourceName, dataSource);
        } catch (Exception e) {
            // enabled配置不为true的时候会报该错,即IOC中没有数据源bean
            // 不会加载到targetDataSources动态数据源集合中,如果注解有使用该数据源的key,则会使用默认的数据源
            log.warn("如果配置文件没有启用{}数据源,请忽略!",sourceName);
            log.error("IOC中没有{},请检查数据源配置类beanName是否正确!",beanName);
        }
    }

    /**
     * 去除监控页面底部的广告
     */
    @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("<a.*?banner\"></a><br/>", "");
                text = text.replaceAll("powered.*?shrek.wang</a>", "");
                response.getWriter().write(text);
            }

            @Override
            public void destroy() {
            }
        };
        FilterRegistrationBean registrationBean = new FilterRegistrationBean();
        registrationBean.setFilter(filter);
        registrationBean.addUrlPatterns(commonJsPattern);
        return registrationBean;
    }
}

8.启动类关闭自动配置数据源

数据源需要上面的配置文件手动配置,所以需要排除SpringBoot的数据源自动装配

package com.feizhaiyou.demo;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;

/**
 * @author Jason
 */
// TODO 关闭数据源自动装配
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
@MapperScan(basePackages = "com.feizhaiyou.demo.dao")
public class DataSourceApplication {
    public static void main(String[] args) {
        SpringApplication.run(DataSourceApplication.class, args);
        System.out.println("=========多数据源切换Demo启动==========");
    }
}

9.其他工具类

通过bean名称从IOC中获取对应的bean

package com.feizhaiyou.demo.ustils;

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.stereotype.Component;

/**
 * spring工具类 方便在非spring管理环境中获取bean
 *
 * @author Jason
 */
@Component
public final class SpringUtils implements BeanFactoryPostProcessor
{
    /** Spring应用上下文环境 */
    private static ConfigurableListableBeanFactory beanFactory;

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException
    {
        SpringUtils.beanFactory = beanFactory;
    }

    /**
     * 获取对象
     *
     * @param name
     * @return Object 一个以所给名字注册的bean的实例
     * @throws BeansException
     *
     */
    @SuppressWarnings("unchecked")
    public static <T> T getBean(String name) throws BeansException
    {
        return (T) beanFactory.getBean(name);
    }

    /**
     * 获取类型为requiredType的对象
     *
     * @param clz
     * @return
     * @throws BeansException
     *
     */
    public static <T> T getBean(Class<T> 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 NoSuchBeanDefinitionException
     *
     */
    public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException
    {
        return beanFactory.isSingleton(name);
    }

    /**
     * @param name
     * @return Class 注册对象的类型
     * @throws NoSuchBeanDefinitionException
     *
     */
    public static Class<?> getType(String name) throws NoSuchBeanDefinitionException
    {
        return beanFactory.getType(name);
    }

    /**
     * 如果给定的bean名字在bean定义中有别名,则返回这些别名
     *
     * @param name
     * @return
     * @throws NoSuchBeanDefinitionException
     *
     */
    public static String[] getAliases(String name) throws NoSuchBeanDefinitionException
    {
        return beanFactory.getAliases(name);
    }

    /**
     * 获取aop代理对象
     *
     * @param invoker
     * @return
     */
    @SuppressWarnings("unchecked")
    public static <T> T getAopProxy(T invoker)
    {
        return (T) AopContext.currentProxy();
    }
}

添加数据源操作

1.springboot配置文件中追加数据源

application-druid.yml

输入图片说明

      slave2:
        enabled: true
        driverClassName: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://127.0.0.1:3306/nacos?characterEncoding=utf8&serverTimezone=Asia/Shanghai
        username: root
        password: 123456

2.设置对应的数据源key

DataSourceType.java

输入图片说明

3.数据源配置类手动配置新增数据源bean

DruidConfig文件:

输入图片说明

	// TODO 设置数据源bean 读取配置
    // 设置数据源beanName
    @Bean("slave2DataSource")
    // 读取的数据源配置
    @ConfigurationProperties("spring.datasource.druid.slave2")
    // 判断是否读取 enabled: true 读取
    @ConditionalOnProperty(prefix = "spring.datasource.druid.slave2", name = "enabled", havingValue = "true")
    public DataSource slave2DataSource(DruidProperties druidProperties) {
        DruidDataSource dataSource = DruidDataSourceBuilder.create().build();
        return druidProperties.dataSource(dataSource);
    }

4.将数据源bean加载到动态数据源集合中

DruidConfig文件:dataSource方法

输入图片说明

setDataSource(targetDataSources, DataSourceType.SLAVE2.name(), "slave2DataSource");

5.在Service层添加注解使用数据源

该类下面所有的方法都会使用指定key的数据源

输入图片说明

@Service
@DataSource(value = DataSourceType.SLAVE2)
public class DataSource3ServiceImpl implements DataSource3Service {
    @Autowired
    private DataSource3Mapper dataSource3Mapper;
}

测试

数据源展示

master数据源:连接mysql.test库

输入图片说明

输入图片说明

slave数据源:连接oracle库(公司数据库不便展示)

输入图片说明

slave2数据源:连接mysql.nacos库(懒得建了)

输入图片说明

输入图片说明

测试代码

Controller

输入图片说明

Service

输入图片说明

输入图片说明

输入图片说明

mapper

输入图片说明

输入图片说明

输入图片说明

PostMan测试

请求master数据源接口

输入图片说明

输入图片说明

请求slave数据源接口

输入图片说明

输入图片说明

请求slave2数据源接口

输入图片说明

输入图片说明

测试关闭slave2数据源

输入图片说明

输入图片说明

输入图片说明

输入图片说明

使用建议

建议将@Datasource注解加到Service层的实现类上。

建议将操作比较多的数据源设置为默认数据源,即master数据源。

不建议将使用默认(master)数据源的类上添加@Datasource(value = DataSourceType.MASTER)注解。

空文件

简介

该项目是SpringBoot基于Druid连接池,通过AOP实现多数据源的配置与切换,用于代码中链接不同类型的数据库。 展开 收起
取消

发行版

暂无发行版

贡献者

全部

近期动态

加载更多
不能加载更多了
1
https://gitee.com/feizhaiyou/data-source-demo.git
git@gitee.com:feizhaiyou/data-source-demo.git
feizhaiyou
data-source-demo
data-source-demo
master

搜索帮助