Druid动态切换数据源

本文详细介绍了如何在SpringBoot项目中配置DruidDataSource实现主从数据源,包括application.yml配置、动态数据源类、自定义DataSource注解、DataSourceAspect切面以及DataSourceContextHolder工具类的使用。
摘要由CSDN通过智能技术生成

一、配置 application.yml

spring:
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    druid:
      master:
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://192.168.3.100:7001/xxxx?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&nullCatalogMeansCurrent=true
        username: xxx
        password: xxx
      slave1:
        # 从数据源开关/默认关闭
        enabled: true
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://192.168.3.100:7001/xxx?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&nullCatalogMeansCurrent=true
        username: xxx
        password: xxx
      # 初始连接数
            initialSize: 5
            # 最小连接池数量
            minIdle: 10
            # 最大连接池数量
            maxActive: 20
            # 配置获取连接等待超时的时间
            maxWait: 60000
            # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
            timeBetweenEvictionRunsMillis: 60000
            # 配置一个连接在池中最小生存的时间,单位是毫秒
            minEvictableIdleTimeMillis: 300000
            # 配置一个连接在池中最大生存的时间,单位是毫秒
            maxEvictableIdleTimeMillis: 900000
            rewriteBatchedStatements: true
            # 配置检测连接是否有效
            validationQuery: SELECT 1 FROM DUAL
            testWhileIdle: true
            testOnBorrow: false
            testOnReturn: false
            webStatFilter:
                enabled: true
            statViewServlet:
                enabled: true
                # 设置白名单,不填则允许所有访问
                allow:
                url-pattern: /druid/*
                login-username: xxx
                login-password: xxx
            filter:
                stat:
                    enabled: true
                    # 慢SQL记录
                    log-slow-sql: true
                    slow-sql-millis: 1000
                    merge-sql: true
                wall:
                    config:
                        multi-statement-allow: true

二、配置类实现多数据源配置

@Configuration
@Slf4j
public class MultiDataSourceConfig {

    @Bean
    public PlatformTransactionManager platformTransactionManager(DataSource dynamicDataSource) {
        return new DataSourceTransactionManager(dynamicDataSource);
    }

    @Bean
    @Primary
    @DependsOn("primaryDataSource")
    public DataSource dynamicDataSource(@Qualifier(DataSourceType.PRIMARY) DataSource primaryDataSource,
                                        @Qualifier(DataSourceType.SECOND) DataSource secondDataSource) {
        DynamicDataSource dynamicDataSource = new DynamicDataSource();
        // 1.设置默认数据源
        dynamicDataSource.setDefaultTargetDataSource(primaryDataSource);
        // 2.配置多数据源
        Map<Object, Object> map = new HashMap<>();
        map.put(DataSourceType.PRIMARY, primaryDataSource);
        map.put(DataSourceType.SECOND, secondDataSource);
        // 3.存放数据源集
        dynamicDataSource.setTargetDataSources(map);
        return dynamicDataSource;
    }

    @Bean(name = DataSourceType.PRIMARY)
    @ConfigurationProperties(prefix = "spring.datasource.druid.master")
    public DataSource primaryDataSource() {
        log.info("数据库1连接池创建中.......");
        return DruidDataSourceBuilder.create().build();
    }

    @Bean(name = DataSourceType.SECOND)
    @ConfigurationProperties(prefix = "spring.datasource.druid.slave1")
    public DataSource secondDataSource() {
        log.info("数据库2连接池创建中.......");
        return DruidDataSourceBuilder.create().build();
    }
}

三、实现自定义注解

自定义@DataSource注解,用于动态指定方法或整个类执行时应用的数据源。它接受一个字符串参数value,该参数表示目标数据源的名称。

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
@Documented
public @interface DataSource {
    String value() default DataSourceType.PRIMARY;
}

四、定义DataSourceAspect切面类

利用AOP(面向切面编程)技术,在方法执行前动态切换数据源。该切面通过环绕通知(@Around)拦截所有被@DataSource注解标记的方法或类,并在方法执行前后设置和清除相应的数据源。

@Aspect
@Order(value=1)
@Component
@Slf4j
public class DataSourceAspect {

    /** 定义切入点表达式*/
    @Pointcut("@annotation(com.example.zsj.api.aop.DataSource)  || @within(com.example.zsj.api.aop.DataSource) ")
    public void dataSourcePointCut() {
    }

    @Around("dataSourcePointCut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();

        // 尝试获取方法上的@DataSource注解
        DataSource methodAnnotation = method.getAnnotation(DataSource.class);

        // 获取目标类上的@DataSource注解
        DataSource classAnnotation = joinPoint.getTarget().getClass().getAnnotation(DataSource.class);

        // 默认数据源名称
        String dataSource = DataSourceType.PRIMARY; 

        if (methodAnnotation != null) {
            // 方法级别注解优先
            dataSource = methodAnnotation.value();
        } else if (classAnnotation != null) {
            // 类级别注解作为备选
            dataSource = classAnnotation.value();
        }

        try {
            // 设置数据源
            DataSourceContextHolder.setDataSource(dataSource); 
            // 继续执行目标方法
            return joinPoint.proceed(); 
        } finally {
            // 清除线程局部变量中的数据源设置
            DataSourceContextHolder.clearDataSourceType(); 
        }
    }
}

五、定义数据库源枚举类

public class DataSourceType {
    public static final String PRIMARY = "primaryDataSource";
    public static final String SECOND = "secondDataSource";
}

五、定义DataSourceContextHolder工具类存储和获取当前线程数据源的上下文

使用ThreadLocal来存储当前线程使用的数据源标识符。它提供了设置(setDataSource)、获取(getDataSource)和清除(clearDataSourceType)当前线程数据源标识符的方法,确保每个线程都能独立地选择自己的数据源。

public class DataSourceContextHolder {
    /**
     * 创建FastThreadLocal对象,存储当前线程的数据源信息
     */
    private static final FastThreadLocal<String> CONTEXT_HOLDER = new FastThreadLocal<String>();
    /**
     * 设置数据源
     */
    public static void setDataSource(String dataSource) {
        CONTEXT_HOLDER.set(dataSource);
    }
    /**
     * 获取数据源
     */
    public static String getDataSource() {
        return CONTEXT_HOLDER.get();
    }
    /**
     * 清除数据源
     */
    public static void clearDataSourceType() {
        CONTEXT_HOLDER.remove();
    }
}

六、重写determineCurrentLookupKey方法

在每次数据库操作之前,基于当前线程保存的数据源标识符(通过DataSourceContextHolder获取)来动态决定使用哪个数据源。

public class DynamicDataSource extends AbstractRoutingDataSource {
    @Override
    protected Object determineCurrentLookupKey() {
        return DataSourceContextHolder.getDataSource();
    }
}
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

何壹时

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值