springboot 多數據源配置動態切換

在實際開發中,經常可能遇到在一個應用中可能需要訪問多個數據庫的情況,那麼我們就要使用切換數據源來實現我們的功能,也就是多數據源的配置

環境是springboot

  1. 在application-druid.yml配置從庫數據源
# 數據源配置
spring:
    datasource:
        type: com.alibaba.druid.pool.DruidDataSource
        driverClassName: com.mysql.cj.jdbc.Driver
        druid:
            # 主庫數據源
            master:
                url: jdbc:mysql://127.0.0.1:3306/tset1?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
                username: root
                password: 123456
            # 從庫數據源
            slave:
                # 從數據源開關/默認關閉
                enabled: true
                url: jdbc:mysql://127.0.0.1:3306/test2?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
                username: root
                password: 123456
            # 初始連接數
            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
                # 設置白名單,不填則允許所有訪問
                allow:
                url-pattern: /druid/*
                # 控制檯管理用戶名和密碼
                login-username: 
                login-password: 
            filter:
                stat:
                    enabled: true
                    # 慢SQL記錄
                    log-slow-sql: true
                    slow-sql-millis: 1000
                    merge-sql: true
                wall:
                    config:
                        multi-statement-allow: true
  1. 創建一個數據源枚舉類DataSourceType
/**
 * 數據源
 * 
 */
public enum DataSourceType
{
    /**
     * 主庫
     */
    MASTER,

    /**
     * 從庫
     */
    SLAVE
}

  1. 創建一個讀取數據源配置的類MysqlConfig 和 動態數據源DynamicDataSource druid 配置屬性DruidProperties
/**
 * druid 配置多數據源
 *
 */
@Configuration
public class MysqlConfig{
	@Bean
	@ConfigurationProperties("spring.datasource.druid.master")
	public DataSource masterDataSource(DruidProperties druidProperties) {
		DruidDataSource dataSource = DruidDataSourceBuilder.create().build();
		return druidProperties.dataSource(dataSource);
	}

	@Bean
	@ConfigurationProperties("spring.datasource.druid.slave")
	@ConditionalOnProperty(prefix = "spring.datasource.druid.slave", name = "enabled", havingValue = "true")
	public DataSource slaveDataSource(DruidProperties druidProperties) {
		DruidDataSource dataSource = DruidDataSourceBuilder.create().build();
		return druidProperties.dataSource(dataSource);
	}

	@Bean(name = "dynamicDataSource")
	@Primary
	public DynamicDataSource dataSource(DataSource masterDataSource, DataSource slaveDataSource) {
		Map<Object, Object> targetDataSources = new HashMap<>();
		targetDataSources.put(DataSourceType.MASTER.name(), masterDataSource);
		targetDataSources.put(DataSourceType.SLAVE.name(), slaveDataSource);
		return new DynamicDataSource(masterDataSource, targetDataSources);
	}	
}

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import com.alibaba.druid.pool.DruidDataSource;

/**
 * druid 配置屬性
 *
 */
@Configuration
public class DruidProperties {
	@Value("${spring.datasource.druid.initialSize}")
	private int initialSize;

	@Value("${spring.datasource.druid.minIdle}")
	private int minIdle;

	@Value("${spring.datasource.druid.maxActive}")
	private int maxActive;

	@Value("${spring.datasource.druid.maxWait}")
	private int maxWait;

	@Value("${spring.datasource.druid.timeBetweenEvictionRunsMillis}")
	private int timeBetweenEvictionRunsMillis;

	@Value("${spring.datasource.druid.minEvictableIdleTimeMillis}")
	private int minEvictableIdleTimeMillis;

	@Value("${spring.datasource.druid.maxEvictableIdleTimeMillis}")
	private int maxEvictableIdleTimeMillis;

	@Value("${spring.datasource.druid.validationQuery}")
	private String validationQuery;

	@Value("${spring.datasource.druid.testWhileIdle}")
	private boolean testWhileIdle;

	@Value("${spring.datasource.druid.testOnBorrow}")
	private boolean testOnBorrow;

	@Value("${spring.datasource.druid.testOnReturn}")
	private boolean testOnReturn;

	public DruidDataSource dataSource(DruidDataSource datasource) {
		/** 配置初始化大小、最小、最大 */
		datasource.setInitialSize(initialSize);
		datasource.setMaxActive(maxActive);
		datasource.setMinIdle(minIdle);

		/** 配置獲取連接等待超時的時間 */
		datasource.setMaxWait(maxWait);

		/** 配置間隔多久才進行一次檢測,檢測需要關閉的空閒連接,單位是毫秒 */
		datasource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);

		/** 配置一個連接在池中最小、最大生存的時間,單位是毫秒 */
		datasource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
		datasource.setMaxEvictableIdleTimeMillis(maxEvictableIdleTimeMillis);

		/**
		 * 用來檢測連接是否有效的sql,要求是一個查詢語句,常用select 'x'。如果validationQuery爲null,testOnBorrow、testOnReturn、testWhileIdle都不會起作用。
		 */
		datasource.setValidationQuery(validationQuery);
		/** 建議配置爲true,不影響性能,並且保證安全性。申請連接的時候檢測,如果空閒時間大於timeBetweenEvictionRunsMillis,執行validationQuery檢測連接是否有效。 */
		datasource.setTestWhileIdle(testWhileIdle);
		/** 申請連接時執行validationQuery檢測連接是否有效,做了這個配置會降低性能。 */
		datasource.setTestOnBorrow(testOnBorrow);
		/** 歸還連接時執行validationQuery檢測連接是否有效,做了這個配置會降低性能。 */
		datasource.setTestOnReturn(testOnReturn);
		return datasource;
	}
}

import java.util.Map;
import javax.sql.DataSource;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import com.tfun.common.config.datasource.DynamicDataSourceContextHolder;

/**
 * 動態數據源
 * 
 */
public class DynamicDataSource extends AbstractRoutingDataSource
{
    public DynamicDataSource(DataSource defaultTargetDataSource, Map<Object, Object> targetDataSources)
    {
        super.setDefaultTargetDataSource(defaultTargetDataSource);
        super.setTargetDataSources(targetDataSources);
        super.afterPropertiesSet();
    }

    @Override
    protected Object determineCurrentLookupKey()
    {
        return DynamicDataSourceContextHolder.getDataSourceType();
    }
}
  1. 在需要使用多數據源方法或類上添加@DataSource註解,其中value用來表示數據源
    Service或Mapper方法上添加@DataSource註解
@Service
@DataSource(value = DataSourceType.SLAVE)
public class TfunActivityServiceImpl implements ITfunActivityService {
}
  1. 對於特殊情況可以通過DynamicDataSourceContextHolder手動實現數據源切換
public List<TfunUser> selectUserList(TfunUser user){
    DynamicDataSourceContextHolder.setDataSourceType(DataSourceType.SLAVE.name());
    List<TfunUser> userList = tfunUserMapper.selectUserList(user);
    DynamicDataSourceContextHolder.clearDataSourceType();
    return userList;
}
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 數據源切換處理
 * 
 */
public class DynamicDataSourceContextHolder
{
    public static final Logger log = LoggerFactory.getLogger(DynamicDataSourceContextHolder.class);

    /**
     * 使用ThreadLocal維護變量,ThreadLocal爲每個使用該變量的線程提供獨立的變量副本,
     *  所以每一個線程都可以獨立地改變自己的副本,而不會影響其它線程所對應的副本。
     */
    private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>();

    /**
     * 設置數據源的變量
     */
    public static void setDataSourceType(String dsType)
    {
        log.info("切換到{}數據源", dsType);
        CONTEXT_HOLDER.set(dsType);
    }

    /**
     * 獲得數據源的變量
     */
    public static String getDataSourceType()
    {
        return CONTEXT_HOLDER.get();
    }

    /**
     * 清空數據源變量
     */
    public static void clearDataSourceType()
    {
        CONTEXT_HOLDER.remove();
    }
}

以上就是多數據源的配置

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章