java多数据源

配置类

package com.nuzar.fcms.common.core.config;

import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.convert.DurationUnit;

import javax.sql.DataSource;
import java.time.Duration;
import java.time.temporal.ChronoUnit;
import java.util.HashMap;
import java.util.Map;

/**
 * 数据源配置信息,使用 Hikari
 *
 * @author tangang
 */
@Getter
@Setter
public class HikariDataSourceProperties {

    private String poolName;

    private String driverClassName;

    private String url;

    private String username;

    private String password;

    private int maxPoolSize = 100;

    private int minIdle = 1;

    @DurationUnit(ChronoUnit.MINUTES)
    private Duration idleTimeout = Duration.ofMinutes(10);

    @DurationUnit(ChronoUnit.MINUTES)
    private Duration validationTimeout = Duration.ofMinutes(10);

    @DurationUnit(ChronoUnit.MINUTES)
    private Duration maxLifetime = Duration.ofMinutes(30);

    @DurationUnit(ChronoUnit.MINUTES)
    private Duration connectionTimeout = Duration.ofMinutes(30);

    /**
     * 拓展配置
     */
    private Map<String, String> dataSourceProperty = new HashMap<>();

    public DataSource dataSource() {
        HikariConfig config = new HikariConfig();
        config.setPoolName(getPoolName());
        config.setDriverClassName(getDriverClassName());
        config.setJdbcUrl(getUrl());
        config.setUsername(getUsername());
        config.setPassword(getPassword());
        config.setMinimumIdle(getMinIdle());
        config.setMaximumPoolSize(getMaxPoolSize());
        config.setAutoCommit(true);
        config.setIdleTimeout(getIdleTimeout().toMillis());
        config.setMaxLifetime(getMaxLifetime().toMillis());
        config.setConnectionTimeout(getConnectionTimeout().toMillis());
        config.addDataSourceProperty("cachePrepStmts", "true"); // 是否自定义配置,为true时下面两个参数才生效
        config.addDataSourceProperty("prepStmtCacheSize", "250"); // 连接池大小默认25,官方推荐250-500
        config.addDataSourceProperty("prepStmtCacheSqlLimit", "2048"); // 单条语句最大长度默认256,官方推荐2048
        for (Map.Entry<String, String> entry : dataSourceProperty.entrySet()) {
            String key = entry.getKey(), value = entry.getValue();
            config.addDataSourceProperty(key, value);
        }
        return new HikariDataSource(config);
    }
}
package com.nuzar.fcms.common.core.config;

import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.NestedConfigurationProperty;

import java.util.HashMap;
import java.util.Map;

/**
 * 默认数据源与多数据源配置文件
 *
 * @author tangang
 */
@Getter
@Setter
@ConfigurationProperties(prefix = "spring.datasource")
public class CompositeDataSourceProperties extends HikariDataSourceProperties {


    @NestedConfigurationProperty
    private Map<String, HikariDataSourceProperties> multi = new HashMap<>();
}

注入bean

package com.nuzar.fcms.common.core.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.sql.DataSource;

/**
 * 数据源配置 bean
 *
 * @author tangang
 */
@Configuration
@EnableConfigurationProperties(CompositeDataSourceProperties.class)
public class DataSourceConfiguration {

    @Autowired
    private CompositeDataSourceProperties compositeDataSourceProperties;

    /**
     * 多数据源
     */
    @Bean
    public DataSource dataSource() {
        return new MultiRoutingDataSource(compositeDataSourceProperties);
    }

}

多数据源实现

package com.nuzar.fcms.common.core.config;

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

import java.util.HashMap;
import java.util.Map;

/**
 * 支持路由多数据源的 datasource
 *
 * @author tangang
 */
public class MultiRoutingDataSource extends AbstractRoutingDataSource {

    /**
     * 数据源配置
     */
    private CompositeDataSourceProperties compositeDataSourceProperties;

    public MultiRoutingDataSource(CompositeDataSourceProperties compositeDataSourceProperties) {
        this.compositeDataSourceProperties = compositeDataSourceProperties;
        init();
    }

    /**
     * 初始化数据源
     */
    private void init() {
        //设置默认数据源
        setDefaultTargetDataSource(compositeDataSourceProperties.dataSource());

        Map<Object, Object> multiDataSource = new HashMap<>();
        for (Map.Entry<String, HikariDataSourceProperties> entry : compositeDataSourceProperties.getMulti().entrySet()) {
            multiDataSource.put(entry.getKey(), entry.getValue().dataSource());
        }
        //设置路由目标数据源
        setTargetDataSources(multiDataSource);
    }

    /**
     * 确定当前数据源路由key
     */
    @Override
    protected Object determineCurrentLookupKey() {
        return MultiRoutingDataSourceContextHolder.getRoutingKey();
    }
}

多数据源路由 key 的线程上下文

package com.nuzar.fcms.common.core.config;

/**
 * 多数据源路由 key 的线程上下文,通过 @MultiDataSource 的切面变化,不建议直接调用
 *
 * @author tangang
 */
class MultiRoutingDataSourceContextHolder {
    private static final ThreadLocal<String> routingKeyThreadLocal = new ThreadLocal<>();

    public static void setRoutingKey(String routingKey) {
        routingKeyThreadLocal.set(routingKey);
    }

    public static String getRoutingKey() {
        return routingKeyThreadLocal.get();
    }

    public static void clear() {
        routingKeyThreadLocal.remove();
    }
}

多数据源切面

package com.nuzar.fcms.common.core.config;


import com.nuzar.fcms.common.core.annotations.MultiDataSource;
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.AnnotatedElementUtils;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.lang.reflect.Method;
import java.lang.reflect.Parameter;

/**
 * 多数据源切面
 */
@Aspect
@Component
public class MultiRoutingDataSourceAspectConfig {

    private ExpressionParser parser = new SpelExpressionParser();

    @Pointcut("@annotation(com.nuzar.fcms.common.core.annotations.MultiDataSource)")
    public void multiDataSource() {
    }

    /**
     * 围绕 @MultiDataSource 注解标识的方法设置数据源
     */
    @Around("multiDataSource()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        String routingKey = getRoutingKey(methodSignature, joinPoint.getArgs());
        //设置当前线程上下文的数据源路由key
        MultiRoutingDataSourceContextHolder.setRoutingKey(routingKey);
        try {
            return joinPoint.proceed();
        } finally {
            //清除当前线程上下文的数据源路由key
            MultiRoutingDataSourceContextHolder.clear();
        }

    }

    /**
     * 根据 @MultiDataSource 注解的属性获取路由 key
     *
     * @param methodSignature
     * @param args
     * @return
     */
    private String getRoutingKey(MethodSignature methodSignature, Object[] args) {
        if (methodSignature == null) {
            return null;
        }
        MultiDataSource multiDataSource = AnnotatedElementUtils.getMergedAnnotation(methodSignature.getMethod(), MultiDataSource.class);
        if (multiDataSource == null) {
            multiDataSource = AnnotatedElementUtils.getMergedAnnotation(methodSignature.getDeclaringType(), MultiDataSource.class);
        }
        if (multiDataSource == null) {
            return null;
        }
        //优先使用 name 硬编码的 key
        if (StringUtils.hasText(multiDataSource.name())) {
            return multiDataSource.name();
        }
        if (StringUtils.hasText(multiDataSource.key())) {
            return parseSpel(methodSignature.getMethod(), args, multiDataSource.key());
        }

        return null;
    }

    /**
     * 解析 spel 表达式
     *
     * @param method    方法
     * @param arguments 参数
     * @param spel      表达式
     * @return 执行spel表达式后的结果
     */
    private String parseSpel(Method method, Object[] arguments, String spel) {
        Parameter[] parameters = method.getParameters();
        EvaluationContext context = new StandardEvaluationContext();
        for (int len = 0; len < parameters.length; len++) {
            context.setVariable(parameters[len].getName(), arguments[len]);
        }
        Expression expression = parser.parseExpression(spel);
        return expression.getValue(context, String.class);

    }
}

注解用法

  /**
     * 在场箱汇总列表
     */
    @MultiDataSource(name = "ph")
    List<PFYardContainerSumVw> yardSummaryList(String iycCstCoperc);

yml配置

spring:
  #oracle数据源
  datasource:
    driver-class-name: oracle.jdbc.driver.OracleDriver
    url: jdbc:oracle:thin:@//10.166.0.161:1521/hftest
    username: hfpttest
    password: HFpttt#0923#Ss
    max-pool-size: 100
    idle-timeout: 60000
    validation-timeout: 60000
    max-lifetime: 360000

    multi: 
      ph:
        driver-class-name: oracle.jdbc.driver.OracleDriver
        url: jdbc:oracle:thin:@//10.166.0.34:1521/hftest
        username: hftops
        password: Hftops#220110
        max-pool-size: 100
        idle-timeout: 60000
        validation-timeout: 60000
        max-lifetime: 360000

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值