java 多数据源

1、pom 导入依赖

        <!--druid连接池-->       
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.10</version>
        </dependency>
        <!--切面-->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.8.13</version>
        </dependency>

2、多个数据源配置  以 mysql 5 为例

spring:
  datasource:
    druid:
      master:
        url: jdbc:mysql://ip:3306/数据库名?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&useSSL=false&&serverTimezone=GMT%2B8&useSSL=false
        username: root
        password: root
      second:
        url: jdbc:mysql://ip2:3306/数据库名?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&useSSL=false&&serverTimezone=GMT%2B8&useSSL=false
        username: root
        password: root
      driver-class-name: com.mysql.jdbc.Driver
      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

3、配置数据源 默认连接

import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
import org.mybatis.spring.annotation.MapperScan;
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;

/**
 * 数据源配置类
 */
@Configuration
@MapperScan("com.xxx.xxx.mapper")
public class DruidConfig {

    /**
     * 主库数据源bean,和spring.datasource.druid.master配置绑定
     *
     * @return
     */
    @Bean(name = CommonConstant.MASTER)
    @ConfigurationProperties("spring.datasource.druid.master")
    public DataSource masterDataSource() {
        DruidDataSource master = DruidDataSourceBuilder.create().build();
        return master;
    }

    /**
     * 数据库2
     * @return
     */
    @Bean(name = CommonConstant.second)
    @ConfigurationProperties("spring.datasource.druid.second")
    public DataSource secondDataSource() {
        DruidDataSource second= DruidDataSourceBuilder.create().build();
        return second;
    }


    /**
     * 动态数据源bean
     *
     * @return
     */
    @Bean
    @Primary
    public DynamicDataSource dynamicDataSource() {
        //创建一个存放数据源的map
        Map<Object, Object> dataSourceMap = new HashMap<>(2);
        //将上述两个数据源存放到map中
        dataSourceMap.put(CommonConstant.MASTER, masterDataSource());
        dataSourceMap.put(CommonConstant.second, secondDataSource());

        //设置动态数据源,默认为master配置的数据源,并指定数据源的map
        DynamicDataSource dynamicDataSource = new DynamicDataSource();
        dynamicDataSource.setDefaultTargetDataSource(masterDataSource());
        System.out.println("设置默认数据源");
        dynamicDataSource.setTargetDataSources(dataSourceMap);


        //将数据源信息备份在defineTargetDataSources中
        dynamicDataSource.setDefineTargetDataSources(dataSourceMap);

        return dynamicDataSource;
    }
}

4、数据源配置常量类


/**
 * 数据源配置常量类
 */
public class CommonConstant {
    /**
     * 默认数据源标识
     */
    public static final String MASTER = "master";
    /**
     * 从数据源标识
     */
    public static final String second= "second";
}

5、注解类

import java.lang.annotation.*;

@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface Ds
{
    /**
     * 切换数据源名称
     */
    public String value() default CommonConstant.MASTER;
}

6、环绕通知

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

import java.util.Objects;

@Aspect
@Component
public class DataSourceAspect {

    // 设置Ds注解的切点表达式,所有Ds都会触发当前环绕通知
    @Pointcut("@annotation(com.xxx.xxx.config.druid.Ds)")
    public void dynamicDataSourcePointCut(){

    }

    //环绕通知
    @Around("dynamicDataSourcePointCut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable{
        //获取数据源的key
        String key = getDefineAnnotation(joinPoint).value();
        //将数据源设置为该key的数据源
        DynamicDataSourceHolder.setDynamicDataSourceKey(key);
        try {                                                           
            return joinPoint.proceed();
        } finally {
            //使用完成后切回master
            DynamicDataSourceHolder.removeDynamicDataSourceKey();
        }
    }

    /**
     * 先判断方法的注解,后判断类的注解,以方法的注解为准
     * @param joinPoint
     * @return
     */
    private Ds getDefineAnnotation(ProceedingJoinPoint joinPoint){
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        Ds dataSourceAnnotation = methodSignature.getMethod().getAnnotation(Ds.class);
        if (Objects.nonNull(methodSignature)) {
            return dataSourceAnnotation;
        } else {
            Class<?> dsClass = joinPoint.getTarget().getClass();
            return dsClass.getAnnotation(Ds.class);
        }
    }

}

7、数据源信息处理

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

import java.util.Map;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class DynamicDataSource extends AbstractRoutingDataSource {
    //备份所有数据源信息,
    private Map<Object, Object> defineTargetDataSources;

    /**
     * 返回当前线程需要用到的数据源bean
     */
    @Override
    protected Object determineCurrentLookupKey() {
        return DynamicDataSourceHolder.getDynamicDataSourceKey();
    }
}

8、数据源切换处理类

import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import lombok.extern.slf4j.Slf4j;

/**
 * 数据源切换处理类
 *
 */
@Slf4j
public class DynamicDataSourceHolder {
    /**
     * 为每个线程存放当前数据源的ThreadLocal
     */
    private static final ThreadLocal<String> DYNAMIC_DATASOURCE_KEY = new ThreadLocal<>();

    /**
     * 为当前线程切换数据源
     */
    public static void setDynamicDataSourceKey(String key) {
//        log.info("数据源切换key:{}", key);
        DYNAMIC_DATASOURCE_KEY.set(key);
    }

    /**
     * 获取动态数据源的名称,默认情况下使用mater数据源
     */
    public static String getDynamicDataSourceKey() {
        String key = DYNAMIC_DATASOURCE_KEY.get();
        if (ObjectUtils.isEmpty(key)) {
            key = CommonConstant.MASTER;
        }
//        log.info("获取数据源,key:{}", key);
        return key;
    }

    /**
     * 将ThreadLocal置空,移除当前数据源
     */
    public static void removeDynamicDataSourceKey() {
//        log.info("移除数据源:{}", DYNAMIC_DATASOURCE_KEY.get());
        DYNAMIC_DATASOURCE_KEY.remove();
    }
}

9、以上就可以使用多个数据源自由切换了

// 切换数据源
DynamicDataSourceHolder.setDynamicDataSourceKey(CommonConstant.second);

// 切回主数据源
DynamicDataSourceHolder.setDynamicDataSourceKey(CommonConstant.MASTER);

10、如需配置动态数据源 如下

数据源对象类


/**
 * 数据源对象类
 */
public class DataSourceInfo {

    private String userName;
    private String passWord;
    private String url;
    private String dataSourceKey;

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getPassWord() {
        return passWord;
    }

    public void setPassWord(String passWord) {
        this.passWord = passWord;
    }

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public String getDataSourceKey() {
        return dataSourceKey;
    }

    public void setDataSourceKey(String dataSourceKey) {
        this.dataSourceKey = dataSourceKey;
    }

    @Override
    public String toString() {
        return "DataSourceInfo{" +
                "userName='" + userName + '\'' +
                ", passWord='" + passWord + '\'' +
                ", url='" + url + '\'' +
                ", dataSourceKey='" + dataSourceKey + '\'' +
                '}';
    }
}

11、数据源配置工具类

import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.support.json.JSONUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.sql.SQLException;
import java.util.Map;

/**
 * 数据源管理工具类
 */
@Slf4j
@Component
public class DataSourceUtil {

    @Resource
    DynamicDataSource dynamicDataSource;

    /**
     * 测试数据源是否可用,如果可用即直接返回
     * @param dataSourceInfo
     * @return
     * @throws SQLException
     */
    public DruidDataSource createDataSourceConnection(DataSourceInfo dataSourceInfo) throws SQLException {
        //创建数据源对象
        DruidDataSource druidDataSource = new DruidDataSource();
        druidDataSource.setUrl(dataSourceInfo.getUrl());
        druidDataSource.setUsername(dataSourceInfo.getUserName());
        druidDataSource.setPassword(dataSourceInfo.getPassWord());
        druidDataSource.setBreakAfterAcquireFailure(true);
        druidDataSource.setConnectionErrorRetryAttempts(0);
        try {
            //尝试连接数据源
            druidDataSource.getConnection(2000);
            log.info("数据源:{}连接成功", JSONUtils.toJSONString(dataSourceInfo));
            return druidDataSource;
        } catch (SQLException e) {
            log.error("数据源 {} 连接失败,用户名:{},密码 {}",dataSourceInfo.getUrl(),dataSourceInfo.getUserName(),dataSourceInfo.getPassWord());
            return null;
        }
    }

    /**
     * 将外部数据源存到dynamicDataSource并调用afterPropertiesSet刷新
     * @param druidDataSource
     * @param dataSourceName
     */
    public void addDefineDynamicDataSource(DruidDataSource druidDataSource, String dataSourceName){
        Map<Object, Object> defineTargetDataSources = dynamicDataSource.getDefineTargetDataSources();
        //存到defineTargetDataSources这个map中
        defineTargetDataSources.put(dataSourceName, druidDataSource);
        dynamicDataSource.setTargetDataSources(defineTargetDataSources);
        //调用afterPropertiesSet重新遍历map中的数据源键值对存到resolvedDataSources中
        dynamicDataSource.afterPropertiesSet();
    }
}

12、使用

  DataSourceInfo dataSourceInfo = new DataSourceInfo();
        dataSourceInfo.setUrl("url");
        dataSourceInfo.setPassWord("password");
        dataSourceInfo.setUserName("username");
        dataSourceInfo.setDataSourceKey("xxx");
        DataSourceUtil dataSourceUtil = new DataSourceUtil();
        DruidDataSource dataSourceConnection = dataSourceUtil.createDataSourceConnection(dataSourceInfo);
        dataSourceUtil.addDefineDynamicDataSource(dataSourceConnection,dataSourceInfo.getDataSourceKey());

//切换
 DynamicDataSourceHolder.setDynamicDataSourceKey(dataSourceInfo.getDataSourceKey());

原文地址:不记得了,如有知情,请联系我

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值