SpringBoot+Hirika 实现动态数据源

1、实现原理

AbstractRoutingDataSource中,determineTargetDataSource 方法通过数据源的标识获取当前数据源;determineCurrentLookupKey方法则是获取数据源标识,实现动态切换数据源,需要实现determineCurrentLookupKey方法,动态提供数据源标识即可。这边使用AOP识别方法上的注解进行数据源切换。没用注解使用默认数据源。

2、需要的类

实现AbstractRoutingDataSource的方法,使用DataSourceContextHolder统一数据源管理

DynamicDataSource

package com.hsshy.beam.common.mutidatasource;

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

/**
 * 动态数据源
 *
 * @author hs
 * @date 2019年2月12日
 */
public class DynamicDataSource extends AbstractRoutingDataSource {

	@Override
	protected Object determineCurrentLookupKey() {
		return DataSourceContextHolder.getDataSourceType();
	}

}

DataSourceContextHolder

package com.hsshy.beam.common.mutidatasource;

/**
 * datasource的上下文
 *
 * @author hs
 * @date 2019年2月12日
 */
public class DataSourceContextHolder {

    private static final ThreadLocal contextHolder = new ThreadLocal();

    /**
     * 设置数据源类型
     *
     * @param dataSourceType 数据库类型
     */
    public static void setDataSourceType(String dataSourceType) {
        contextHolder.set(dataSourceType);
    }

    /**
     * 获取数据源类型
     */
    public static String getDataSourceType() {
        return contextHolder.get();
    }

    /**
     * 清除数据源类型
     */
    public static void clearDataSourceType() {
        contextHolder.remove();
    }
}

数据库数据源配置

a、默认数据源 HikariProperties配置

package com.hsshy.beam.common.config;


import com.zaxxer.hikari.HikariDataSource;

/**
 * 

数据库数据源配置

* @author hs * @date 2019年2月12日 */ public class HikariProperties { private String url = "jdbc:mysql://127.0.0.1:3306/beam?autoReconnect=true&useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull"; private String username = "root"; private String password = "root"; private String driverClassName = "com.mysql.cj.jdbc.Driver"; private long connectionTimeout = 60000L; private long idleTimeout = 60000L; private long validationTimeout = 3000L; private long maxLifetime = 60000L; private int maximumPoolSize = 60; private int minimumIdle = 10; public void config(HikariDataSource dataSource) { dataSource.setJdbcUrl(url); dataSource.setUsername(username); dataSource.setPassword(password); dataSource.setDriverClassName(driverClassName); dataSource.setDriverClassName(driverClassName); dataSource.setConnectionTimeout(connectionTimeout); dataSource.setIdleTimeout(idleTimeout); dataSource.setValidationTimeout(validationTimeout); dataSource.setMaxLifetime(maxLifetime); dataSource.setMaximumPoolSize(maximumPoolSize); dataSource.setMinimumIdle(minimumIdle); dataSource.setReadOnly(false); } public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } 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 getDriverClassName() { return driverClassName; } public void setDriverClassName(String driverClassName) { this.driverClassName = driverClassName; } public long getConnectionTimeout() { return connectionTimeout; } public void setConnectionTimeout(long connectionTimeout) { this.connectionTimeout = connectionTimeout; } public long getIdleTimeout() { return idleTimeout; } public void setIdleTimeout(long idleTimeout) { this.idleTimeout = idleTimeout; } public long getValidationTimeout() { return validationTimeout; } public void setValidationTimeout(long validationTimeout) { this.validationTimeout = validationTimeout; } public long getMaxLifetime() { return maxLifetime; } public void setMaxLifetime(long maxLifetime) { this.maxLifetime = maxLifetime; } public int getMaximumPoolSize() { return maximumPoolSize; } public void setMaximumPoolSize(int maximumPoolSize) { this.maximumPoolSize = maximumPoolSize; } public int getMinimumIdle() { return minimumIdle; } public void setMinimumIdle(int minimumIdle) { this.minimumIdle = minimumIdle; } }

b、第二个数据源配置 MutiDataSourceProperties

package com.hsshy.beam.common.config;
import com.zaxxer.hikari.HikariDataSource;

/**
 * 默认多数据源配置
 *
 * @author fengshuonan
 * @date 2017-08-16 10:02
 */
public class MutiDataSourceProperties {

    private String url = "jdbc:mysql://127.0.0.1:3306/biz?autoReconnect=true&useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull";

    private String username = "root";

    private String password = "root";

    private long connectionTimeout = 60000L;

    private long idleTimeout = 60000L;

    private long validationTimeout = 3000L;

    private long maxLifetime = 60000L;

    private int maximumPoolSize = 60;

    private int minimumIdle = 10;

    private String filters = "log4j,wall,mergeStat";

    private String driverClassName = "com.mysql.cj.jdbc.Driver";

    private String[] dataSourceNames = {"dataSourceBeam", "dataSourceBiz"};

    public void config(HikariDataSource dataSource) {
        dataSource.setJdbcUrl(url);
        dataSource.setUsername(username);
        dataSource.setPassword(password);
        dataSource.setDriverClassName(driverClassName);
        dataSource.setConnectionTimeout(connectionTimeout);
        dataSource.setIdleTimeout(idleTimeout);
        dataSource.setValidationTimeout(validationTimeout);
        dataSource.setMaxLifetime(maxLifetime);
        dataSource.setMaximumPoolSize(maximumPoolSize);
        dataSource.setMinimumIdle(minimumIdle);
        dataSource.setReadOnly(false);


    }


    public String getUrl() {
        return url;
    }

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

    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 long getConnectionTimeout() {
        return connectionTimeout;
    }

    public void setConnectionTimeout(long connectionTimeout) {
        this.connectionTimeout = connectionTimeout;
    }

    public long getIdleTimeout() {
        return idleTimeout;
    }

    public void setIdleTimeout(long idleTimeout) {
        this.idleTimeout = idleTimeout;
    }

    public long getValidationTimeout() {
        return validationTimeout;
    }

    public void setValidationTimeout(long validationTimeout) {
        this.validationTimeout = validationTimeout;
    }

    public long getMaxLifetime() {
        return maxLifetime;
    }

    public void setMaxLifetime(long maxLifetime) {
        this.maxLifetime = maxLifetime;
    }

    public int getMaximumPoolSize() {
        return maximumPoolSize;
    }

    public void setMaximumPoolSize(int maximumPoolSize) {
        this.maximumPoolSize = maximumPoolSize;
    }

    public int getMinimumIdle() {
        return minimumIdle;
    }

    public void setMinimumIdle(int minimumIdle) {
        this.minimumIdle = minimumIdle;
    }

    public String getDriverClassName() {
        return driverClassName;
    }

    public void setDriverClassName(String driverClassName) {
        this.driverClassName = driverClassName;
    }

    public String getFilters() {
        return filters;
    }

    public void setFilters(String filters) {
        this.filters = filters;
    }

    public String[] getDataSourceNames() {
        return dataSourceNames;
    }

    public void setDataSourceNames(String[] dataSourceNames) {
        this.dataSourceNames = dataSourceNames;
    }
}

多数据源配置 MultiDataSourceConfig

package com.hsshy.beam.common.config;
import com.hsshy.beam.common.mutidatasource.DynamicDataSource;
import com.hsshy.beam.common.mutidatasource.aop.MultiSourceExAop;
import com.zaxxer.hikari.HikariDataSource;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.annotation.EnableTransactionManagement;

import java.sql.SQLException;
import java.util.HashMap;

/**
 * 多数据源配置
*

* 注:由于引入多数据源,所以让spring事务的aop要在多数据源切换aop的后面 * * @author hs * @Date 2019/2/12 21:58 */ @Configuration @ConditionalOnProperty(prefix = "beam.muti-datasource", name = "open", havingValue = "true") @EnableTransactionManagement(order = 2) public class MultiDataSourceConfig { @Bean @ConfigurationProperties(prefix = "beam.muti-datasource") public MutiDataSourceProperties mutiDataSourceProperties() { return new MutiDataSourceProperties(); } @Bean @ConfigurationProperties(prefix = "spring.datasource") public HikariProperties hikariProperties() { return new HikariProperties(); } @Bean public MultiSourceExAop multiSourceExAop() { return new MultiSourceExAop(); } /** * guns的数据源 */ private HikariDataSource dataSource(HikariProperties druidProperties) { HikariDataSource dataSource = new HikariDataSource(); druidProperties.config(dataSource); return dataSource; } /** * 多数据源,第二个数据源 */ private HikariDataSource bizDataSource(HikariProperties druidProperties, MutiDataSourceProperties mutiDataSourceProperties) { HikariDataSource dataSource = new HikariDataSource(); druidProperties.config(dataSource); mutiDataSourceProperties.config(dataSource); return dataSource; } /** * 多数据源连接池配置 */ @Bean public DynamicDataSource mutiDataSource(HikariProperties druidProperties, MutiDataSourceProperties mutiDataSourceProperties) { HikariDataSource dataSourceBeam = dataSource(druidProperties); HikariDataSource bizDataSource = bizDataSource(druidProperties, mutiDataSourceProperties); try { dataSourceBeam.getConnection(); bizDataSource.getConnection(); } catch (SQLException sql) { sql.printStackTrace(); } DynamicDataSource dynamicDataSource = new DynamicDataSource(); HashMap hashMap = new HashMap<>(); hashMap.put(mutiDataSourceProperties.getDataSourceNames()[0], dataSourceBeam); hashMap.put(mutiDataSourceProperties.getDataSourceNames()[1], bizDataSource); dynamicDataSource.setTargetDataSources(hashMap); dynamicDataSource.setDefaultTargetDataSource(dataSourceBeam); return dynamicDataSource; } }

数据源标识 DataSource

package com.hsshy.beam.common.mutidatasource.annotion;

import java.lang.annotation.*;

/**
 * 
 * 多数据源标识
 *
 * @author hs
 * @date 2019年2月12日
 */
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD })
public @interface DataSource {

	String name() default "";
}

AOP动态切换数据源 MultiSourceExAop

package com.hsshy.beam.common.mutidatasource.aop;
import com.hsshy.beam.common.config.MutiDataSourceProperties;
import com.hsshy.beam.common.mutidatasource.DataSourceContextHolder;
import com.hsshy.beam.common.mutidatasource.annotion.DataSource;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
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.beans.factory.annotation.Autowired;
import org.springframework.core.Ordered;
import java.lang.reflect.Method;

/**
 * 多数据源切换的aop
 *
 * @author hs
 * @date 2019年2月12日
 */
@Aspect
public class MultiSourceExAop implements Ordered {

    private Logger log = LoggerFactory.getLogger(this.getClass());

    @Autowired
    MutiDataSourceProperties mutiDataSourceProperties;

    @Pointcut(value = "@annotation(com.hsshy.beam.common.mutidatasource.annotion.DataSource)")
    private void cut() {

    }

    @Around("cut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {

        Signature signature = point.getSignature();
        MethodSignature methodSignature = null;
        if (!(signature instanceof MethodSignature)) {
            throw new IllegalArgumentException("该注解只能用于方法");
        }
        methodSignature = (MethodSignature) signature;

        Object target = point.getTarget();
        Method currentMethod = target.getClass().getMethod(methodSignature.getName(), methodSignature.getParameterTypes());

        DataSource datasource = currentMethod.getAnnotation(DataSource.class);
        if (datasource != null) {
            DataSourceContextHolder.setDataSourceType(datasource.name());
            log.debug("设置数据源为:" + datasource.name());
        } else {
            DataSourceContextHolder.setDataSourceType(mutiDataSourceProperties.getDataSourceNames()[0]);
            log.debug("设置数据源为:dataSourceCurrent");
        }

        try {
            return point.proceed();
        } finally {
            log.debug("清空数据源信息!");
            DataSourceContextHolder.clearDataSourceType();
        }
    }


    /**
     * aop的顺序要早于spring的事务
     */
    @Override
    public int getOrder() {
        return 1;
    }

}

多数据源枚举 DatasourceEnum

package com.hsshy.beam.common.constant;

/**
 * 
 * 多数据源的枚举
 *
 * @author hs
 * @date 2019年2月12日
 */
public interface DatasourceEnum {

	String DATA_SOURCE_GUNS = "dataSourceBeam";			//beam数据源
	
	String DATA_SOURCE_BIZ = "dataSourceBiz";			//其他业务的数据源
}

配置文件 application.yml

server:
   port: 8081
spring:
  profiles: local
  datasource:
      url: jdbc:mysql://127.0.0.1:3306/beam?autoReconnect=true&useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=CONVERT_TO_NULL&useSSL=false&serverTimezone=GMT%2B8
      username: root
      password: 123456
      filters: log4j,wall,mergeStat
      hikari:
        readOnly: false
        connectionTimeout: 60000
        idleTimeout: 60000
        validationTimeout: 3000
        maxLifetime: 60000
        loginTimeout: 5
        maximumPoolSize: 60
        minimumIdle: 10
  redis:
    database: 0
    host: 127.0.0.1
    port: 6379
    password:      # 密码(默认为空)
    timeout: 6000ms  # 连接超时时长(毫秒)

##多数据源情况的配置
beam:
  muti-datasource:
    open: true
    url: jdbc:mysql://127.0.0.1:3306/test?autoReconnect=true&useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=CONVERT_TO_NULL&useSSL=false&serverTimezone=GMT%2B8
    username: root
    password: 123456
    dataSourceNames:
      - dataSourceBeam
      - dataSourceBiz

只有一个数据源可以使用以下配置,即可使用Hikari数据库连接池

spring:
  profiles: local
  datasource:
      url: jdbc:mysql://127.0.0.1:3306/beam?autoReconnect=true&useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=CONVERT_TO_NULL&useSSL=false&serverTimezone=GMT%2B8
      username: root
      password: root
      filters: log4j,wall,mergeStat
      hikari:
        readOnly: false
        connectionTimeout: 60000
        idleTimeout: 60000
        validationTimeout: 3000
        maxLifetime: 60000
        loginTimeout: 5
        maximumPoolSize: 60
        minimumIdle: 10

你可能感兴趣的:(SpringBoot,连接池,Hirika)