數據庫讀寫分離

實現原理:

master/slave數據庫都對應不同的操作名稱,執行Dao層方法時,判斷方法名是否以給定的master操作名稱開頭(比如:add, delete, save, delete), 如果是,則用master DB,如果不是則用slave DB

實現步驟:

利用SpringAbstractRoutingDataSource解決多數據源的問題,使用AOP動態切換數據源。

a. 配置 master/slave等多個數據源

b. 寫一個DynamicDataSource類繼承AbstractRoutingDataSource,並實現etermineCurrentLookupKey

方法。配置一個動態數據源

c. dataSourceThreadLocal 綁定,利用ThreadLocal解決線程安全問題

d. 利用AOP, 編寫DataSourceAdvice類,實現根據方法名動態切換數據源的功能。其中使用<util:set>來配置master Db對應的操作名稱。

代碼實現及分析:

a.配置數據源:

<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="lililocations">
<value>classpath:jdbc.properties</value>
</property>
</bean>

<bean id="masterDataSource" ...> … </bean>
<bean id="slaveDataSource" ...> … </bean>

<bean id="dataSource" class="com.lili.a.ds.DynamicDataSource">
<property name="targetDataSources">
<map key-type="java.lang.String">
<entry key="master" value-ref="masterDataSource" />
<entry key="slave" value-ref="slaveDataSource" />
</map>
</property>
<property name="defaultTargetDataSource" ref="slaveDataSource" />
</bean>

b.DynamicDataSource類:

繼承AbstractRoutingDataSource, AbstractRoutingDataSourcejavax.sql.DataSource的子類 ,於是我們自然地去看他的getConnection 方法:

public Connection getConnection() throws SQLException {

return determineTargetDataSource().getConnection();

}

我發現原來奧妙就在determineTargetDataSource()裏:

通過看源碼,我發現determineTargetDataSource()用到了我們需要進行實現的抽象方法determineCurrentLookupKey(),該方法返回使用的DataSourcekey值。根據key值從resolvedDataSources這個map 中獲取對應的value值。如果找不到,則使用默認的resolvedDefaultDataSource

resolvedDataSources是一個map,程序會將配置中的targetDataSources 這個Map中的key-value複製到resolvedDataSources。同樣的會將 defaultTargetDataSourceDataSource值賦給 defaultTargetDataSource

public class MyDynamicDataSource extends AbstractRoutingDataSource {

@Override

protected Object determineCurrentLookupKey() {

    return DynamicDataSourceHolder.getDataSouce();

   }

}

c. DynamicDataSourceHolder類:

public class DynamicDataSourceHolder {

    private static final ThreadLocal<String> datasource = new ThreadLocal<String>();

    public static void setDataSource(String dataSource) {
        datasource.set(dataSource);
 } public static String getDataSouce(){ String dataSource = datasource .get(); if (dataSource == null) { setMaster("master"); } returndatasource.get().toString(); }}

d.AOP

<bean id="dataSourceAdvice" class="com.lili.test.ds.DataSourceAdvice">
        <property name="MasterKeys" ref="MasterKeys"></property>
</bean>

<util:set id="MasterKeys">
        <value>delete</value>
        <value>add</value>
        <value>update</value>
        <value>save</value>
</util:set>
<aop:config>
        <aop:pointcut id="pc" expression="execution(* com.lili.a.dao..*.*(..)) || execution(* com.lili.a.mybatis..*.*(..))" />
        <aop:advisor pointcut-ref="pc" advice-ref="dataSourceAdvice" order="1" />
</aop:config>



public class DataSourceAdvice implements MethodBeforeAdvice{
    private Set<String> MasterKeys = new HashSet<String>();
    public void setMasterKeys(Set<String> MasterKeys) {
        this.MasterKeys = MasterKeys;
    }

    @Override
    public void before(Method method, Object[] arg, Object target) throws Throwable {
        boolean isMaster = false;
        String methodName = method.getName();
        for(String accessMasterKey : MasterKeys) {
            if (methodName.startsWith(accessMasterKey)) {
                isMaster = true;
                break;
            }
        }
           DynamicDataSourceHolder.setDataSource("master");
       } else {
           DynamicDataSourceHolder.setDataSource("slave");
       }
    }
}

發佈了25 篇原創文章 · 獲贊 3 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章