1. 程式人生 > >Spring動態資料來源切換例項

Spring動態資料來源切換例項

實現原理

1.擴充套件Spring的抽象類AbstractRoutingDataSource(該類充當了DataSource的路由中介,在執行時,能根據某key值來動態切換到真正的DataSource上),重寫determineCurrentLookupkey()抽象方法。determineCurrentLookupkey()方法的返回值是我們所要用到的DataSource的key值,有了這個key值,我們就可以從map(該map的key和value值是在配置檔案中配置好存入的)中取出對應的DataSource,如果找不到,就用預設資料來源。

2.編寫資料來源操作類的介面,設定預設資料來源為null,因為當程式沒有找到關聯的資料來源時,就會呼叫預設資料來源

package com.shadow.system.base.source;

import org.aspectj.lang.JoinPoint;

/**
 * 資料來源切換介面
 * 
 * @author shadow
 * @create 2013.04.03
 */
public interface DataSourceEntry {

	// 預設資料來源
	public final static String DEFAULT_SOURCE = null;

	/**
	 * 還原資料來源
	 * 
	 * @param joinPoint
	 */
	public void restore(JoinPoint join);

	/**
	 * 設定資料來源
	 * 
	 * @param dataSource
	 */
	public void set(String source);

	/**
	 * 獲取資料來源
	 * 
	 * @return String
	 */
	public String get();

	/**
	 * 清空資料來源
	 */
	public void clear();
}

3.寫個實現類,從當前執行緒取出資料來源名
package com.shadow.system.base.source;

import org.aspectj.lang.JoinPoint;

import com.shadow.system.dictionary.DynamicTypeEntry;

/**
 * 資料來源切換實現類類
 * 
 * @author shadow
 * @create 2013.04.03
 */
public class DataSourceEntryImpl implements DynamicTypeEntry {

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

	public void clear() {
		local.remove();
	}

	public String get() {
		return local.get();
	}

	public void restore(JoinPoint join) {
		local.set(DEFAULT_SOURCE); // 設定null資料來源
	}

	public void set(String source) {
		local.set(source);
	}

}

4.擴充套件AbstractRoutingDataSource類,並注入DataSourceEntry,重寫裡面的determineCurrentLookupkey()方法,實現動態切換資料來源。
package com.shadow.system.base.source;

import javax.annotation.Resource;

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

/**
 * 獲取資料來源(依賴SPRING框架)
 * 
 * @author shadow
 * @create 2013.04.03
 */
public class DynamicDataSource extends AbstractRoutingDataSource {

	private DataSourceEntry dataSourceEntry;

	@Override
	protected Object determineCurrentLookupKey() {
		return this.dataSourceEntry.get();
	}

	@Resource
	public void setDataSourceEntry(DataSourceEntry dataSourceEntry) {
		this.dataSourceEntry = dataSourceEntry;
	}

}

5.最後就是配置.xml檔案,以後只需要直接管理DynamicDataSource這個介面就可以了,至於它內部是使用的是哪個資料來源是不需要關注的。寫的切面還原是為了保證每次呼叫完另外的資料來源都會還原成預設資料來源,防止有的人忘記設定回預設的,導致其他程式碼出問題。專案中,為了精簡,可以單獨分離出來jdbc.xml資料來源配置檔案。
	<!-- JDBC模板 -->
	<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
		<property name="dataSource" ref="dynamicDataSource" />
	</bean>

	<!-- 獲取資料來源配置 -->
	<bean
		class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
		<property name="locations">
			<value>classpath:properties/jdbc.properties</value>
		</property>
	</bean>

	<!-- 配置動態資料來源 -->
	<bean id="dynamicDataSource" class="com.shadow.system.base.source.DynamicDataSource">
		<!-- 通過key-value的形式來關聯資料來源 -->
		<property name="targetDataSources">
			<map key-type="java.lang.String">
				<entry value-ref="C3P0_COMMON" key="C3P0_COMMON"></entry>
			</map>
		</property>
		<property name="defaultTargetDataSource" ref="C3P0_COMMON" />
	</bean>

	<!-- database config -->
	<bean id="C3P0_COMMON" class="com.mchange.v2.c3p0.ComboPooledDataSource"
		destroy-method="close" lazy-init="true">
		<property name="driverClass" value="${sqlite.driverClass}" />
		<property name="jdbcUrl" value="${sqlite.jdbcUrl}" />
		<property name="minPoolSize" value="${sqlite.miniPoolSize}" />
		<property name="maxPoolSize" value="${sqlite.maxPoolSize}" />
		<property name="initialPoolSize" value="${sqlite.initialPoolSize}" />
		<property name="maxIdleTime" value="${sqlite.maxIdleTime}" />
		<property name="acquireIncrement" value="${sqlite.acquireIncrement}" />
		<property name="acquireRetryAttempts" value="${sqlite.acquireRetryAttempts}" />
		<property name="acquireRetryDelay" value="${sqlite.acquireRetryDelay}" />
		<property name="testConnectionOnCheckin" value="${sqlite.testConnectionOnCheckin}" />
		<property name="testConnectionOnCheckout" value="${sqlite.testConnectionOnCheckout}" />
		<property name="autoCommitOnClose" value="${sqlite.autoCommitOnClose}" />
		<property name="idleConnectionTestPeriod" value="${sqlite.idleConnectionTestPeriod}" />
		<property name="checkoutTimeout" value="${sqlite.checkoutTimeout}" />
		<property name="numHelperThreads" value="${sqlite.numHelperThreads}" />
	</bean>
	
	<!-- 配置資料來源切換實現類 -->
	<bean id="dataSourceEntry" class="com.shadow.system.base.source.DataSourceEntryImpl" />

	<!-- 切面還原預設資料來源 -->
	<aop:config>
		<aop:aspect id="dataSourceHolderAdviceAspect" ref="dataSourceEntry">
			<aop:after method="restore"
				pointcut=<span><span class="string">"execution(* com.shadow.mvc.service.SmsService.saveOrderSMS(..))"</span><span></span></span> />
		</aop:aspect>
	</aop:config>

6.在程式中如何變換資料來源,可以在切面中通過before,來檢測切換資料來源的方法。也可以在程式中直接使用DataSourceEntry的set方法。
	public List<Sms> findForAll() {
		dataSourceEntry.set(DynamicTypeEntry.C3P0_ACCESS);
		return this.smsDao.queryForAll();
	}