spring boot 注解方式配置多数据源与使用


现在老板的需求是那么的讲究,需要读写分离,那么我们如何配置多个数据源的,下面用springboot搭框架,配置多个数据源。

参考链接:https://blog.csdn.net/xuSir_1/article/details/78986972;

 

1.springboot配置多数据源 首先看一下application-dev.yml 配置

 

spring:
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driverClassName: com.mysql.jdbc.Driver
    druid:
      first:  #数据源1
             url: jdbc:mysql://192.168.1.252:3306/b2q?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8
             username: root
             password: b2q123456
      second:  #数据源2
            url: jdbc:mysql://localhost:3306/b2q?allowMultiQueries=true&useUnicode=true&useSSL=false&characterEncoding=UTF-8
            username: root
            password: root

       # password: Quali-tyCz87897013!
      initial-size: 10
      max-active: 100
      min-idle: 10
      max-wait: 60000
      pool-prepared-statements: true
      max-pool-prepared-statement-per-connection-size: 20
      time-between-eviction-runs-millis: 60000
      min-evictable-idle-time-millis: 300000
      lower_case_table_names: 0
      #validation-query: SELECT 1 FROM DUAL
      test-while-idle: true
      test-on-borrow: false
      test-on-return: false
      stat-view-servlet:
        enabled: true
        url-pattern: /druid/*
        #login-username: admin
        #login-password: admin
      filter:
        stat:
          log-slow-sql: true
          slow-sql-millis: 1000
          merge-sql: false
        wall:
          config:
            multi-statement-allow: true

  

上面的方式实现就已经配置了两个 数据源了,下面来看下代码的实现

2、配置一个注解,方便使用,直接在需要配置的方法上面加上数据源即可

@Target({ ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataSource {
    String name() default "";
}

 2-1:配置管理多数据源的名称,方便管理。

/**
 * 增加多数据源,在此配置 
 */
public interface DataSourceNames {
    String FIRST = "first";
    String SECOND = "second"; 
}

 3、动态数据源加载

package com.wsh.b2q.admin.datasources;

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

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

/**
 * 动态数据源
 * @author huojg
 */
public class DynamicDataSource extends AbstractRoutingDataSource {
    private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();

    public DynamicDataSource(DataSource defaultTargetDataSource, Map<Object, Object> targetDataSources) {
        super.setDefaultTargetDataSource(defaultTargetDataSource);
        super.setTargetDataSources(targetDataSources);
        super.afterPropertiesSet();
    }

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

    public static void setDataSource(String dataSource) {
        contextHolder.set(dataSource);
    }

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

    public static void clearDataSource() {
        contextHolder.remove();
    }

}

  

这里有必要说一下AbstractRoutingDataSource这个类,加载一个图片:

可以看到AbstractRoutingDataSource获取数据源之前会先调用determineCurrentLookupKey方法查找当前的lookupKey,这个lookupKey就是数据源标识。
因此通过重写这个查找数据源标识的方法就可以让spring切换到指定的数据源了。
3-1:重要一点:把上面的代码加载到配置中

package com.wsh.b2q.admin.datasources;

import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;

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

/**
 * 配置多数据源
 * @author huojg
 */
@Configuration
public class DynamicDataSourceConfig {

    @Bean
    @ConfigurationProperties("spring.datasource.druid.first")
    public DataSource firstDataSource(){
        return DruidDataSourceBuilder.create().build();
    }

    @Bean
    @ConfigurationProperties("spring.datasource.druid.second")
    public DataSource secondDataSource(){
        return DruidDataSourceBuilder.create().build();
    }

    @Bean
    @Primary
    public DynamicDataSource dataSource(DataSource firstDataSource, DataSource secondDataSource) {
        Map<Object, Object> targetDataSources = new HashMap<>();
        targetDataSources.put(DataSourceNames.FIRST, firstDataSource);
        targetDataSources.put(DataSourceNames.SECOND, secondDataSource);
        return new DynamicDataSource(firstDataSource, targetDataSources);
    }
}

 4、最最重要的一步,就是使用spring的aop原理,切面方式加载数据源

package com.wsh.b2q.admin.datasources.aspect;

import com.wsh.b2q.admin.datasources.DataSourceNames;
import com.wsh.b2q.admin.datasources.DynamicDataSource;
import com.wsh.b2q.admin.datasources.annotation.DataSource;
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.Ordered;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

/**
 * 多数据源,切面处理类
 * @author huojg
 */
@Aspect
@Component
public class DataSourceAspect implements Ordered {
    protected Logger logger = LoggerFactory.getLogger(getClass());

    @Pointcut("@annotation(com.wsh.b2q.admin.datasources.annotation.DataSource)")
    public void dataSourcePointCut() {

    }

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

        DataSource ds = method.getAnnotation(DataSource.class);
        if(ds == null){
            DynamicDataSource.setDataSource(DataSourceNames.FIRST);
            logger.debug("set datasource is " + DataSourceNames.FIRST);
        }else {
            DynamicDataSource.setDataSource(ds.name());
            logger.debug("set datasource is " + ds.name());
        }

        try {
            return point.proceed();
        } finally {
            DynamicDataSource.clearDataSource();
            logger.debug("clean datasource");
        }
    }

    @Override
    public int getOrder() {
        return 1;
    }
}

5、最后一步就是使用了在你的service的实现类 serviceImpl上面进行注解,这里是重点(我刚开一直放在dao上面,因为我用的是mybatis,以为就是要放在这个上面,结果一直出不来,最后才知道应该放在serviceImpl上面)

@Override
	@DataSource(name="second")
	public List<IntegralExchangeRule> list(Map<String,Object> map) {
		return dao.list(map);
	}

 OK,现在已经全部配置完成,可以使用了

有必要说一下:我上面的DataSourceAspect这个类里面around方法里面,已经默认是数据源1,如果你不配置@DaeSource(name=""),它默认会使用第一个数据源,否则的话,按照你的数据源名称去使用的。

 


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM