SpringBoot學習筆記(15):動態數據源切換

SpringBoot學習筆記(15):動態數據源切換

數據源

  Java的javax.sql.DataSource接口提供了一種處理數據庫鏈接的標準方法。一般,DataSource使用URL和一些憑據來創建數據庫鏈接。html

  SpringBoot默認提供了針對內存數據庫的數據源,如H二、hqldb、Derby等。java

配置數據源信息

  當咱們引入spring-boot-start-jdbc時,SpringBoot會默認使用其綁定的Tomcat的數據源
mysql

  DataSource可由spring.datasource.*中的外部配置屬性控制。例如,您能夠在application.properties中聲明如下部分:spring

spring.datasource.url=jdbc:mysql://localhost/test
spring.datasource.username=dbuser
spring.datasource.password=dbpass
spring.datasource.driver-class-name=com.mysql.jdbc.Driver

  固然,咱們也能夠是使用基於Java代碼的配置方法:sql

    @Bean
    public DataSource mysqlDataSource(){
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://localhost:3306/demo");
        dataSource.setUsername("root");
        dataSource.setPassword("123456");
        return dataSource;
    }

使用第三方數據源

  有時候,咱們須要使用第三方的數據源,如Druid、C3P0等,在SpringBoot中也很簡單。數據庫

  以Druid爲例,首先添加Maven依賴:安全

<dependencies>
		<!-- 添加MySQL依賴 -->
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
		</dependency>
		<!-- 添加JDBC依賴 -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-jdbc</artifactId>
		</dependency>
		<!-- 添加Druid依賴 -->
		<dependency>
			<groupId>com.alibaba</groupId>
			<artifactId>druid</artifactId>
			<version>1.1.6</version>
		</dependency>
</dependencies>

  配置數據庫鏈接信息便可:bash

@Configuration
public class DataSourceConfig {
 
	@Autowired
	private Environment env;
 
	@Bean
	public DataSource getDataSource() {
		DruidDataSource dataSource = new DruidDataSource();
		dataSource.setUrl(env.getProperty("spring.datasource.url"));
		dataSource.setUsername(env.getProperty("spring.datasource.username"));
		dataSource.setPassword(env.getProperty("spring.datasource.password"));
		return dataSource;
	}
}

 

動態新增數據源

  有時候,咱們須要在程序運行過程當中動態增長或改變數據源。app

改變數據源

  Spring2.0 引入了AbstractRoutingDataSource,該類能夠充當DataSource的路由中介,能在運行時,動態切換當前數據源。以下爲其核心代碼,可見他是根據determineCurrentLookupKey()獲取Key值,來從resolvedDataSources中找到要切換的數據源ide

private Map<Object, Object> targetDataSources;
private Map<Object, DataSource> resolvedDataSources;

protected DataSource determineTargetDataSource() {
        Assert.notNull(this.resolvedDataSources, "DataSource router not initialized");
        Object lookupKey = this.determineCurrentLookupKey();
        DataSource dataSource = (DataSource)this.resolvedDataSources.get(lookupKey);
        if (dataSource == null && (this.lenientFallback || lookupKey == null)) {
            dataSource = this.resolvedDefaultDataSource;
        }

        if (dataSource == null) {
            throw new IllegalStateException("Cannot determine target DataSource for lookup key [" + lookupKey + "]");
        } else {
            return dataSource;
        }
    }

  而這個determineCurrentLookupKey函數須要咱們自已來實現,因此咱們要定義一個子類DynamicRoutingDataSource來繼承AbstractRoutingDataSource,而後實現這個方法,其實就是返回一個Key字符串!。

  爲了保證線程安全,咱們能夠把數據源的Key信息保存在ThreadLocal中,以下:

public class DynamicDataSourceContextHolder {
    private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>() {
        @Override
        protected String initialValue() {
            return "dynamic_db0";
        }
    };

    /**
     * To switch DataSource
     *
     * @param key the key
     */
    public static void setDataSourceKey(String key) {
        contextHolder.set(key);
    }

    /**
     * Get current DataSource
     *
     * @return data source key
     */
    public static String getDataSourceKey() {
        return contextHolder.get();
    }

    /**
     * To set DataSource as default
     */
    public static void clearDataSourceKey() {
        contextHolder.remove();
    }

}

  這樣,determineCurrentLookupKey就能夠以下定義:

    @Override
    protected Object determineCurrentLookupKey() {
        System.out.println("Current DataSource is [{}]"+ DynamicDataSourceContextHolder.getDataSourceKey());
        return DynamicDataSourceContextHolder.getDataSourceKey();
    }

  因此咱們在使用時,只須要使用DynamicDataSourceContextHolder.setDataSourceKey(),並能夠切換數據源。

新增數據源

  新增數據源,其實就是更新targetDataSources字段,一個簡單的Demo以下:

/**
     * 動態增長數據源
     *
     * @param map 數據源屬性
     * @return
     */
    public synchronized boolean addDataSource(Map<String, String> map) {
        try {
            String database = map.get("database");//獲取要添加的數據庫名
            if (database==null||database.equals("")) return false;
            if (DynamicRoutingDataSource.isExistDataSource(database)) return true;
            DriverManagerDataSource dataSource = new DriverManagerDataSource();
            dataSource.setDriverClassName("com.mysql.jdbc.Driver");
            dataSource.setUrl(map.get("url"));
            dataSource.setUsername(map.get("username"));
            dataSource.setPassword(map.get("password"));

            Map<Object, Object> targetMap = DynamicRoutingDataSource.targetDataSources;
            targetMap.put(database, dataSource);
            // 當前 targetDataSources 與 父類 targetDataSources 爲同一對象 因此不須要set
//          this.setTargetDataSources(targetMap);
            this.afterPropertiesSet();
        } catch (Exception e) {
            logger.error(e.getMessage());
            return false;
        }
        return true;
    }

 

配置動態數據源

  咱們能夠將其以Bean形式配置,並在其中設置默認數據源。

    @Bean("dynamicDataSource")
    public DynamicRoutingDataSource dynamicDataSource() {
        DynamicRoutingDataSource dynamicRoutingDataSource = new DynamicRoutingDataSource();
        Map<Object, Object> dataSourceMap = new HashMap<>();
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://localhost:3306/demo");
        dataSource.setUsername("root");
        dataSource.setPassword("123456");
        dataSourceMap.put("DEMO", dataSource);
        dynamicRoutingDataSource.setDefaultTargetDataSource(dataSource);// 設置默認數據源
        dynamicRoutingDataSource.setTargetDataSources(dataSourceMap);
        return dynamicRoutingDataSource;
    }

  以後,咱們在代碼中即可以執行新增數據源、切換數據源操做等等。

參考文檔:

相關文章
相關標籤/搜索