1. 程式人生 > >spring動態創建數據源

spring動態創建數據源

trac 啟動 ext max mic ali null query 持久層框架

項目業務,在程序的運行過程中,可能會存在新的數據庫添鏈接加進來,從新數據庫鏈接中讀取數據。

spring為多數據源提供了一個抽象類AbstractRoutingDataSource,該類中只有一個抽象方法determineCurrentLookupKey()需要由我們實現。

假設我們創建一個類DynimaticDataSource,繼承AbstractRoutingDataSource,並重寫determineCurrentLookupKey()方法。

spring啟動初始化DynimaticDataSource:

1、可以通過spring的自動註入,對AbstractRoutingDataSource類註入相應的屬性,註入屬性不是必須的,可以通過繼承後重寫方法來重設對這些屬性的調用。

2、註入後spring會執行DynimaticDataSource中的protected方法afterPropertiesSet(),該方法先判斷屬性targetDataSources不能為null,否則拋出異常"Property ‘targetDataSources‘ is required"。然後將該屬性(類型為map<object,object>)的值全部轉換到屬性resolvedDataSources(類型為map<Object, DataSource>)中去。如果屬性defaultTargetDataSource不為null,將其轉換為DataSource類型並賦值給屬性defaultTargetDataSource。

經過以上處理後,屬性resolvedDataSources中會被存放我們添加的數據源,該屬性是一個map集合,key為Object類型,value為數據源。

使用DynimaticDataSource類:

1、調用該類的public方法getConnection()來獲取連接。

2、getConnection被重寫,方法中會先調用protected方法determineTargetDataSource()。該方法先判斷屬性resolvedDataSources不為null,否則拋出異常"DataSource router not initialized"。然後調用由子類重寫的抽象方法determineCurrentLookupKey()獲取dataSource在resolvedDataSources中對應的key。

3、根據key從resolvedDataSources中獲取數據源,如果resolvedDataSources中不存在,再判斷lenientFallback為true(默認為true,可以設置)或key為null,返回默認數據源resolvedDefaultDataSource。否則拋出異常"Cannot determine target DataSource for lookup key [" + key+ "]"。

4、調用獲取數據源的getConnection()方法獲取連接。

在初始化時指定多數據源案例代碼:

1、創建一個類DynimaticDataSource,繼承AbstractRoutingDataSource,並重寫determineCurrentLookupKey()方法。該方法負責判斷當前線程使用哪一種數據源。這是最簡單的一種實現方法,不重寫任何非抽象方法。

public class DynamicDatasource extends AbstractRoutingDataSource{

    private static Map<Object, Object> targetDataSources;
    
    protected DruidDataSource dataSource;
    
    @Override
    protected String determineCurrentLookupKey() {
      //指定使用哪個數據源
return DataSourceUtils.getDbtype(); } }

2、srping配置文件(部分)

    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
        <property name="url" value="${jdbc.url}" />
        <property name="username" value="${jdbc.username}" />
        <property name="password" value="${jdbc.password}" />
        <property name="driverClassName" value="${jdbc.driver}" />
        <property name="maxActive" value="50" /><!-- 最大連接池數 -->
        <property name="minIdle" value="5" /><!-- 最小連接池數 -->
    </bean>
    
    <bean id="dataSource2" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
        <property name="url" value="${jdbc.url2}" />
        <property name="username" value="${jdbc.username2}" />
        <property name="password" value="${jdbc.password2}" />
        <property name="driverClassName" value="${jdbc.driver}" />
        <property name="maxActive" value="50" /><!-- 最大連接池數 -->
        <property name="minIdle" value="5" /><!-- 最小連接池數 -->
    </bean>
    
    <bean id="multDataSource" class="com.ftpSystem.dao.DynamicDatasource">
        <property name="targetDataSources">
            <map >
                <entry value-ref="dataSource" key="masterDataSource"></entry>
                <entry value-ref="dataSource2" key="yangDataSource"></entry>
            </map>
        </property>
    <property name="defaultTargetDataSource" ref="dataSource"></property>
</bean>

3、使用數據源,我這裏使用jdbcTemple,可以使用其他持久層框架,方式同單數據源配置一致。

    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="multDataSource"></property>
    </bean>

4、切換數據源類

public class DataSourceUtils {

    private static final ThreadLocal<String> local = new ThreadLocal<String>();
    
    public static String getDbtype() {
        return local.get();
    }
    
    public static void setDbtype(String dbtype) {
        local.set(dbtype);
    }
    
    public static void clear() {
        local.remove();
    }
}

5、客戶端代碼

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext-dao.xml")
public class TestDao {

    @Autowired
    private JdbcTemplate jdbcTemplate;
    
    @Test
    public void Test(){
        System.out.println("ok");
        int i = (Integer) jdbcTemplate.queryForObject("select count(*) from t_gg_zsdw", Integer.class);
        System.out.println("i="+i);
        DataSourceUtils.setDbtype("dataSource2");//切換數據源至dataSource2
        i = (Integer) jdbcTemplate.queryForObject("select count(*) from t_gg_zsdw", Integer.class);
        System.out.println("i="+i);
    }
}

由於我們已經在spring的配置文件中指定了屬性defaultTargetDataSource,因此程序會默認使用該數據源。然後我們執行一次後切換為dataSource2,之後的執行會改為使用dataSource2的數據源。

以上方式可以對程序配置多數據源,但是不方便之處在程序初始化之時就指定所有的數據源,無法運行時添加。

程序動態配置多數據源:

該方式與上一種的區別在於重寫AbstractRoutingDataSource類的子類DynimaticDataSource

下班了,明天繼續

spring動態創建數據源