Springboot+Druid配置多数据源

Druid数据库多数据源

Spring的多数据源支持—AbstractRoutingDataSource,AbstractRoutingDataSource定义了抽象的determineCurrentLookupKey方法,子类实现此方法,来确定要使用的数据源

Druid 实现多数据源支持,核心是Overwrite AbstractRoutingDataSource 的 determineCurrentLookupKey 方法

public abstract class AbstractRoutingDataSource extends AbstractDataSource implements InitializingBean {
	 protected DataSource determineTargetDataSource() {
		Assert.notNull(this.resolvedDataSources, "DataSource router not initialized");
		Object lookupKey = determineCurrentLookupKey();
		DataSource dataSource = this.resolvedDataSources.get(lookupKey);
		if (dataSource == null && (this.lenientFallback || lookupKey == null)) {
			dataSource = this.resolvedDefaultDataSource;
		}
		if (dataSource == null) {
			throw new IllegalStateException("Cannot determine target DataSource for 	             lookup key [" + lookupKey + "]");
		}
		return dataSource;
	}

        // 确定当前要使用的数据源
        protected abstract Object determineCurrentLookupKey();
}

配置多数据源

以springboot框架为基础使用aop注解的方式依赖Druid配置多数据源

1.添加依赖

<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
	
		<!-- aop注解实现aspectjweaver依赖 -->
		<dependency>
			<groupId>org.aspectj</groupId>
			<artifactId>aspectjweaver</artifactId>
			<version>1.9.6</version>
		</dependency>
		<!-- mybatis-spring-boot-starter -->
		<dependency>
			<groupId>org.mybatis.spring.boot</groupId>
			<artifactId>mybatis-spring-boot-starter</artifactId>
			<version>2.1.3</version>
		</dependency>
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<version>8.0.28</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
		<!-- https://mvnrepository.com/artifact/com.alibaba/druid-spring-boot-starter -->
		<dependency>
			<groupId>com.alibaba</groupId>
			<artifactId>druid-spring-boot-starter</artifactId>
			<version>1.2.8</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-configuration-processor</artifactId>
			<optional>true</optional>
		</dependency>
		<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<version>1.18.12</version>
			<scope>provided</scope>
		</dependency>

2.配置Druid属性

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    type: com.alibaba.druid.pool.DruidDataSource
    first:
      url: jdbc:mysql://localhost:3306/***?useUnicode=true&characterEncoding=utf-                  8&serverTimezone=UTC
      username: root
      password: *****
    second:
      enable: true
      url: jdbc:mysql://localhost:3306/***?useUnicode=true&characterEncoding=utf-                 8&serverTimezone=UTC
      username: root
      password: *****
    druid:
      # 初始连接数
      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
        # 控制台管理用户名和密码
        login-username: admin
        login-password: 123456
      #配置监控统计拦截的filters:stat:监控统计、self4j(使用log4j的记得导入log4j的依赖):日志记录、          wall:防御sql注入 此处配置不能遗漏服务sql监控台不能监控sql
      filters: stat,wall,log4j2
      filter:
        stat:
          enabled: true
          # 慢SQL记录
          log-slow-sql: true
          slow-sql-millis: 1000
          merge-sql: true
        wall:
          enabled: true
          config:
            multi-statement-allow: true

3.配置动态数据源

继承AbstractRoutingDataSource重写determineCurrentLookupKey()方法


/**
 * 动态数据源
 */
public class DynamicDatesource extends AbstractRoutingDataSource {

    public DynamicDatesource(DataSource dataSource,HashMap<Object, Object> map){
        super.setDefaultTargetDataSource(dataSource);
        super.setTargetDataSources(map);
        super.afterPropertiesSet();
    }

    /**
     * 获取key指定数据源
     * @return
     */
    @Override
    protected Object determineCurrentLookupKey() {
        return DynamicDateSourceCut.getLocal();
    }
}

配置数据源

//配置数据源
@Configuration
public class DruidConfig {

    @Bean
    @ConfigurationProperties(prefix = "spring.datasource.first")
    public DataSource firstDataSource(DruidSourceProperties druidSourceProperties){

        return druidSourceProperties.dataSource(new DruidDataSourceWrapper());
    }
    @Bean
    @ConfigurationProperties(prefix = "spring.datasource.second")
    @ConditionalOnProperty(prefix ="spring.datasource.second",name ="enable",havingValue = "true")
    public DataSource secondDataSource(DruidSourceProperties druidSourceProperties){
        return druidSourceProperties.dataSource(new DruidDataSourceWrapper());
    }
    @Bean
    @Primary//在多数据源的时候,使用@Primary注解用于指定其中一个作为主数据源
    public DynamicDatesource setDymaicDatesource(DataSource firstDataSource){
        HashMap<Object, Object> map = new HashMap<>();
        map.put(DateSourceType.FIRST.name(),firstDataSource);
        setDataSource(map,DateSourceType.SECOND.name(),"secondDataSource");
        return new DynamicDatesource(firstDataSource,map);
    }
    /**
     * 设置数据源
     *
     * @param targetDataSources 备选数据源集合
     * @param sourceName 数据源名称
     * @param beanName bean名称
     */
    public void setDataSource(Map<Object, Object> targetDataSources, String sourceName, 		String beanName){
        try {
            DataSource dataSource = SpringUtils.getBean(beanName);
            targetDataSources.put(sourceName, dataSource);
        } catch (Exception e){

        }
    }
}

4.配置数据源切换

package com.example.springboot.data;
/**
*数据源切换 保存每个数据源指定的lookupKey
*/
public class DynamicDateSourceCut {
    /**
     * 使用ThreadLocal维护变量,ThreadLocal为每个使用该变量的线程提供独立的变量副本,
     *  所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。
     */

    private static ThreadLocal<String> threadLocal=new ThreadLocal<>();

    /**
     * 设置数据源的变量
     */
    public static void setLocal(String local){
        threadLocal.set(local);

    }
    /**
     * 获得数据源的变量
     */
    public static String getLocal(){
        return threadLocal.get();

    }
    /**
     * 清空数据源变量
     */
    public static void clearLocl() {
        threadLocal.remove();
    }
}

5.定义aop切面实现

/**
 * 定义数据源切面
 */
@Aspect
@Component
public class DateSourceAspect {

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

    /**
     * 定义切点  使用注解
     * @within类注解
     * @annotation方法注解
     */
    @Pointcut("@annotation(com.example.springboot.aspect.DateSource)"+
             "||@within(com.example.springboot.aspect.DateSource)")
    public void PintCut(){

    }
    /**
     * 通知使用环绕的通知方法
     */
    @Around("PintCut()")
    public Object ResovePoint(ProceedingJoinPoint point) throws Throwable {
        //获取注解
        DateSource dateSource = getDateSource(point);
        if (dateSource!=null){
            DynamicDateSourceCut.setLocal(dateSource.value().name());
        }
        try {
            //执行方法
           return point.proceed();
        } finally {
            // 销毁数据源 在执行方法之后
            DynamicDateSourceCut.clearLocl();
        }

    }

    /**
     * 获取注解
     */
    protected DateSource getDateSource(ProceedingJoinPoint point){
        MethodSignature signature = (MethodSignature) point.getSignature();
        //获取方法或方法类型上的注解
        DateSource annotation;
        //获取class类型上注解
        annotation=AnnotationUtils.findAnnotation(signature.getDeclaringType(),DateSource.class);
        if (annotation==null){
            annotation = AnnotationUtils.findAnnotation(signature.getMethod(), DateSource.class);
        }
        return annotation;
    }
}

定义的注解和注解值


/**
 * 定义注解
 */
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DateSource {
    DateSourceType value()default DateSourceType.FIRST;
}


/**
*使用枚举定义注解的值
*/

public enum DateSourceType {
    //默认数据库
    FIRST,
    SECOND;
}

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值