Spring+MyBatis多數據源的切換

因爲業務需求,這幾天一直在研究Spring+MyBatis多數據源切換。按照網上的各種例子,改了又改,就是切換不成功。最後在其他同事的提醒下,終於發現並解決了問題。現在將多數據源切換的配置和出現並解決的問題記錄下來,爲自己存一份記憶,也爲後來人提供幫助。

一、多數據源的配置

1、兩個類

這兩個類的類名可以根據自己的編碼習慣命名,我的命名及實現代碼如下:

(1)、DataSourceContextHolder:用於進行數據源的獲取、設置及還原

package com.cpms.trasen.common.tk;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 數據源設置類
 * 
 * @author bigdata-cp
 *
 */
public class DataSourceContextHolder {

    // 日誌,與多數據源切換無關
	private final static Logger logger = LoggerFactory.getLogger(DataSourceContextHolder.class);
	
	// 用於切換數據源的線程
	private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>();

	// 設置數據源
	public static void setDataSourceType(String dataSourceType) {
		logger.info("DataSourceContextHolder.setDataSourceType start");
		contextHolder.set(dataSourceType);
		logger.info("DataSourceContextHolder.setDataSourceType end");
	}

	// 獲取現在的數據源
	public static String getDataSourceType() {
		logger.info("implement DataSourceContextHolder.getDataSourceType");
		return contextHolder.get();
	}

	// 還原(清除設置的)數據源
	public static void clearDataSourceType() {
		logger.info("implement DataSourceContextHolder.clearDataSourceType");
		contextHolder.remove();
	}
}

(2)、DynamicDataSource:需要繼承AbstractRoutingDataSource抽象類用於實現數據源的切換

package com.cpms.trasen.common.tk;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

public class DynamicDataSource extends AbstractRoutingDataSource {

	private final static Logger logger = LoggerFactory.getLogger(DynamicDataSource.class);

	@Override
	protected Object determineCurrentLookupKey() {
		logger.info("implement DynamicDataSource.determineCurrentLookupKey");
		System.out.println("此時獲取到的數據源爲:" + DataSourceContextHolder.getDataSourceType());
		return DataSourceContextHolder.getDataSourceType();
	}

}

2、配置文件

SSM框架自帶的如事物、日誌、別名等配置這裏就不多說了,一搜一大把。這裏主要貼多數據源相關的配置代碼。

(1)、數據源文件properties文件:我這裏都是SqlServer數據庫,如果有其他類型的數據庫,手動添加驅動、連接方式和用戶密碼就行。

#SQLServerDriver
driverClassName=com.microsoft.sqlserver.jdbc.SQLServerDriver

#dataSourceA
dataSourceAurl=jdbc:sqlserver://localhost:1433;DatabaseName=trasen_clinicalPathMonitoring
dataSourceAuname=sa
dataSourceApwd=1

#dataSourceB
dataSourceBurl=jdbc:sqlserver://192.168.2.108:1433;DatabaseName=trasen_clinicalPathMonitoring
dataSourceBuname=sa
dataSourceBpwd=1

(2)、spring-mybatis配置文件:基本配置和多數據源配置

<?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:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context"
	xmlns:tx="http://www.springframework.org/schema/tx" xmlns:mvc="http://www.springframework.org/schema/mvc"
	xsi:schemaLocation="http://www.springframework.org/schema/beans      
                        http://www.springframework.org/schema/beans/spring-beans-3.1.xsd      
                        http://www.springframework.org/schema/context      
                        http://www.springframework.org/schema/context/spring-context-3.1.xsd      
                        http://www.springframework.org/schema/mvc      
                        http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd">

	<!-- 掃描service層 -->
	<mvc:annotation-driven />
	<context:component-scan base-package="com.cpms..trasen.*.server" use-default-filters="false" />

	<!-- 引入數據庫配置文件 -->
	<context:property-placeholder location="classpath:sqlServer.properties" />

	<bean id="parentDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
		<property name="driverClass">
			<value>${driverClassName}</value>
		</property>
		<!-- 初始化時獲取的連接數,取值應在minPoolSize與maxPoolSize之間。Default: 3 -->
		<property name="initialPoolSize" value="5" />
		<!-- 連接池中保留的最大連接數。Default: 15 -->
		<property name="maxPoolSize" value="200" />
		<!-- 連接池中保留的最小連接數。 -->
		<property name="minPoolSize" value="5" />
		<!-- 配置當連接池所有連接用完時應用程序getConnection的等待時間 -->
		<property name="checkoutTimeout" value="30000" />
		<!-- 當連接池中的連接耗盡的時候c3p0一次同時獲取的連接數。Default: 3 -->
		<property name="acquireIncrement" value="5" />
		<!-- 最大空閒時間,x秒內未使用則連接被丟棄。若爲0則永不丟棄。Default: 0 -->
		<property name="maxIdleTime" value="601" />
		<!-- 每x秒檢查所有連接池中的空閒連接。Default: 0 -->
		<property name="idleConnectionTestPeriod" value="600" />
		<!-- 連接池在獲得新連接失敗時重試的次數,如果小於等於0則無限重試直至連接獲得成功。Default: 30 -->
		<property name="acquireRetryAttempts" value="5" />
		<!-- 兩次連接中間隔時間,連接池在獲得新連接時的間隔時間。default: 1000 單位ms -->
		<property name="acquireRetryDelay" value="1000" />
		<!-- 測試連接
		<property name="preferredTestQuery">
			<value>SELECT 1</value>
		</property> -->
	</bean>

	<!-- 定義數據源Bean -->
	<bean id="dataSourceA" parent="parentDataSource">
		<property name="jdbcUrl">
			<value>${dataSourceAurl}</value>
		</property>
		<property name="user">
			<value>${dataSourceAuname}</value>
		</property>
		<property name="password">
			<value>${dataSourceApwd}</value>
		</property>
	</bean>

	<bean id="dataSourceB" parent="parentDataSource">
		<property name="jdbcUrl">
			<value>${dataSourceBurl}</value>
		</property>
		<property name="user">
			<value>${dataSourceBuname}</value>
		</property>
		<property name="password">
			<value>${dataSourceBpwd}</value>
		</property>
	</bean>

	<!-- 配置dataSource管理key值和value值對應,默認選擇dataSourceA ,其他配置按照正常的spring mvc 配置即可。 -->
	<bean id="dataSourceSwitcher" class="com.cpms.trasen.common.tk.DynamicDataSource">
		<!-- 默認數據源 -->
		<property name="defaultTargetDataSource" ref="dataSourceA"></property>
		<!-- 通過key-value的形式來關聯數據源 -->
		<property name="targetDataSources">
			<map key-type="java.lang.String">
				<entry key="dataSourceA" value-ref="dataSourceA"></entry>
				<entry key="dataSourceB" value-ref="dataSourceB"></entry>
			</map>
		</property>
	</bean>

	<!-- 註冊SqlSessionFactoryBean -->
	<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
		<property name="dataSource" ref="dataSourceSwitcher" />
		<property name="configLocation" value="classpath:mybatis-config.xml" />
		<!-- 自動掃描mappers.xml文件 -->
		<property name="mapperLocations" value="classpath:com/cpms/trasen/*/dao/mappings/*.xml" />
	</bean>

	<!-- DAO接口所在包名,Spring會自動查找其下的類 -->
	<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
		<property name="basePackage" value="com.cpms.trasen.*.dao" />
		<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />
	</bean>
</beans>

3、數據源的切換

項目需求在用戶登錄成功後,根據用戶切換對應的數據源。博主就是直接寫在用於登錄的service實現類的方法中,該方法調用了一個WebService用於驗證用戶的登錄信息,所以線程不止一個。在設置數據源後的線程的生命週期,在切換數據源之前結束了,進入了另一個線程,所以獲取的數據源爲null導致切換失敗。最後,將設置數據源的方法轉移到了驗證session過期的AOP中,既主線程中,才成功切換數據源。

調用設置數據源的方法如下:數據源字符串也可設置爲類變量,請根據應用場景進行設置。

// "xxx"爲需要切換的數據源字符串,該字符串爲xml中配置的多數據源id
DataSourceContextHolder.setDataSourceType("xxx");

多數據源的配置與切換其實內容很簡單,網上的例子都是可以行得通的。但關鍵的問題是每個項目遇到的情況都會不同,所以導致出現的問題也會不同,需要各自發現和解決。看到的各位有什麼不懂的歡迎留言一起探討。

每天積累一點,付出總會得到回報。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章