1. 程式人生 > >Spring+Mybatis多資料來源配置(四)——AbstractRoutingDataSource實現資料來源動態切換

Spring+Mybatis多資料來源配置(四)——AbstractRoutingDataSource實現資料來源動態切換

有時候需要在程式中動態切換資料來源,那麼這個系列的之前的博文所闡述的方法就不再使用了,總不能通過程式更改config.properties檔案的dataSource的值,然後再重啟web伺服器以便載入applicationContext.xml檔案。這裡講訴的是如何利用AbstractRoutingDataSource進行資料來源動態切換。

首先上applicationContext.xml檔案:

<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:context="http://www.springframework.org/schema/context"
        xmlns:aop="http://www.springframework.org/schema/aop" 
        xmlns:tx="http://www.springframework.org/schema/tx" 
        xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        classpath:/org/springframework/beans/factory/xml/spring-beans-3.0.xsd
        http://www.springframework.org/schema/aop 
        classpath:/org/springframework/aop/config/spring-aop-3.0.xsd
        http://www.springframework.org/schema/context
        classpath:/org/springframework/context/config/spring-context-3.0.xsd
        http://www.springframework.org/schema/tx 
        classpath:/org/springframework/transaction/config/spring-tx-3.0.xsd">

	<!-- IoC配置 -->
	<!-- 掃描類包,將標註Spring註解的類自動轉化Bean,同時完成Bean的注入 -->
	<context:component-scan base-package="com.shr.dao" />
	<context:component-scan base-package="com.shr.service" />
    
	<!-- DAO配置 -->
	<context:property-placeholder location="classpath:config.properties"/>
	<bean id="mysql" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
		<property name="driverClassName" 	value="${mysql_driver}"/>
		<property name="url" 		value="${mysql_url}"/>
		<property name="username" 	value="${mysql_username}"/>
		<property name="password" 	value="${mysql_password}"/>
	</bean>
	<bean id="oracle" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
		<property name="driverClassName" 	value="${ora_driver}"/>
		<property name="url" 		value="${ora_url}"/>
		<property name="username" 	value="${ora_username}"/>
		<property name="password" 	value="${ora_password}"/>
	</bean>
	
	<bean id="dataSource" class="com.shr.dao.datasource.DataSources">
		<property name="targetDataSources">
			<map key-type="java.lang.String">
				<entry value-ref="mysql" key="MYSQL"></entry>
				<entry value-ref="oracle" key="ORACLE"></entry>
			</map>
		</property>
		<property name="defaultTargetDataSource" ref="mysql"></property>
	</bean>

	<bean id="vendorProperties"
		class="org.springframework.beans.factory.config.PropertiesFactoryBean">
		<property name="properties">
			<props>
				<prop key="Oracle">oracle</prop>
				<prop key="MySQL">mysql</prop>
			</props>
		</property>
	</bean>

    <bean id="databaseIdProvider" class="org.apache.ibatis.mapping.VendorDatabaseIdProvider">
        <property name="properties" ref="vendorProperties" />
    </bean>
    <bean name="myBatisSQLInterceptor" class="com.shr.dao.MyBatisSQLInterceptor"></bean>
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <property name="typeAliasesPackage" value="com.shr.dao.pojo,com.shr.dao.model" />
        <property name="databaseIdProvider" ref="databaseIdProvider" />
        <property name="mapperLocations">
            <list>
                <value>classpath:com/shr/dao/resources/mappers/*_mapper.xml</value>
            </list>
        </property>
        <!-- <property name="configLocation" value="/WEB-INF/mybatis-config.xml"/> -->
        <property name="typeHandlersPackage" value="com.shr.dao" />
        <property name="plugins">
        	<list>
        		<ref bean="myBatisSQLInterceptor"/>
        	</list>
        </property>
    </bean>
    
    <!-- 配置事務管理器 -->
    <tx:annotation-driven/>
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="${dataSource}"/>
    </bean>
    
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.shr.dao.mapper"/>
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/> 
        <!-- <property name="markerInterface" value="com.shr.dao.mapper.ITemplateMapper"/> -->
    </bean>
</beans>
我們可以觀察到檔案中多了一段:
	<bean id="dataSource" class="com.shr.dao.datasource.DataSources">
		<property name="targetDataSources">
			<map key-type="java.lang.String">
				<entry value-ref="mysql" key="MYSQL"></entry>
				<entry value-ref="oracle" key="ORACLE"></entry>
			</map>
		</property>
		<property name="defaultTargetDataSource" ref="mysql"></property>
	</bean>
而且sqlSessionFactory的dataSource是關聯到上面這段內容的,而不是通過${dataSource}讀取config.properties檔案的內容獲取的。

這個com.shr.dao.datasource.DataSources是自定義的類,繼承自AbstractRoutingDataSource類,實現其determineCurrentLookupKey()方法。

程式碼如下:

package com.shr.dao.datasource;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
public class DataSources extends AbstractRoutingDataSource
{
	@Override
	protected Object determineCurrentLookupKey()
	{
		return DataSourceSwitch.getDataSourceType();
	}
}
package com.shr.dao.datasource;

public class DataSourceSwitch
{
	private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>();
	
	public static void setDataSourceType(String dataSourceType)
	{
		contextHolder.set(dataSourceType);
	}
	
	public static String getDataSourceType()
	{
		return contextHolder.get();
	}
	
	public static void clearDataSourceType()
	{
		contextHolder.remove();
	}
}
package com.shr.dao.datasource;

public class DataSourceInstances
{
	public static final String MYSQL="MYSQL";
	public static final String ORACLE="ORACLE";
}
同樣,我們通過一個junit測試用例進行驗證:
package com.shr.dao.datasource;

import java.util.List;

import javax.inject.Inject;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.transaction.TransactionConfiguration;
import org.springframework.transaction.annotation.Transactional;

import com.shr.dao.datasource.DataSourceInstances;
import com.shr.dao.datasource.DataSourceSwitch;
import com.shr.dao.model.userManage.UserListInfo;
import com.shr.service.userManage.UserManageService;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("file:WebContent/WEB-INF/applicationContext.xml")
@Transactional
@TransactionConfiguration(transactionManager="transactionManager",defaultRollback=false)
public class DynamicDataSourceTest
{
	@Inject
	private UserManageService userManageService;
	
	@Test
	public void test()
	{
		DataSourceSwitch.setDataSourceType(DataSourceInstances.MYSQL);
		List<UserListInfo> list = userManageService.getUserListInfo();
		for(UserListInfo user : list)
		{
			System.out.println(user.getUser_name());
		}
	}
}
通過改變DataSourceSwitch.setDataSourceType(DataSourceInstances.ORACLE);可以轉換不同的資料來源.