最近公司一個項目需要連接配接兩個資料庫(A和B)操作,有的子產品查詢A庫,有的子產品查詢B庫,是以需要改造下,項目背景用的是SpringMVC+Mybatis+MySQL架構,折騰了兩天後終于搞定了,在這裡記錄過改造過程。
使用場景
多資料源的使用的場景一般有:
- 主從資料庫切換
- 讀寫分離
- 相容舊庫
實作原理
Spring2.x的版本中采用Proxy模式,就是在方案中實作一個虛拟的資料源,并且用它來封裝資料源選擇邏輯,這樣就可以有效地将資料源選擇邏輯從Client中分離出來。Client提供選擇所需的上下文,由虛拟的DynamicDataSource根據Client提供的上下文來實作資料源的選擇。
具體的實作是虛拟的DynamicDataSource僅需繼承AbstractRoutingDataSource實作determineCurrentLookupKey(),該方法傳回需要使用的DataSource的key值,然後根據這個key從resolvedDataSources這個map裡取出對應的DataSource,如果找不到則用預設的resolvedDefaultDataSource。
詳細實作過程
-
修改spring的配置檔案
配置檔案裡需要配置多個資料源,改造後主要配置如下:
<bean id="dataSourceTargetA" class="com.mchange.v2.c3p0.ComboPooledDataSource"
destroy-method="close" scope="singleton">
<property name="driverClass" value="${jdbc.a.driverClassName}" />
<property name="jdbcUrl" value="${jdbc.a.url}" />
<property name="user" value="${jdbc.a.username}" />
<property name="password" value="${jdbc.a.password}" />
<!--當連接配接池中的連接配接耗盡的時候c3p0一次同時擷取的連接配接數。Default: 3 -->
<property name="acquireIncrement" value="${c3p0.acquireIncrement}"></property>
<!--初始化時擷取三個連接配接,取值應在minPoolSize與maxPoolSize之間。Default: 3 -->
<property name="initialPoolSize" value="${c3p0.initialPoolSize}"></property>
<!--最大空閑時間,60秒内未使用則連接配接被丢棄。若為0則永不丢棄。Default: 0 -->
<property name="maxIdleTime" value="${c3p0.maxIdleTime}"></property>
<!--連接配接池中保留的最大連接配接數。Default: 15 -->
<property name="maxPoolSize" value="${c3p0.maxPoolSize}"></property>
<property name="minPoolSize" value="${c3p0.minPoolSize}"></property>
<!--兩次連接配接中間隔時間,機關毫秒。Default: 1000 -->
<property name="acquireRetryDelay" value="1000"></property>
<!--定義在從資料庫擷取新連接配接失敗後重複嘗試的次數。Default: 30 -->
<property name="acquireRetryAttempts" value="60"></property>
<!--擷取連接配接失敗将會引起所有等待連接配接池來擷取連接配接的線程抛出異常。但是資料源仍有效保留,并在下次調用getConnection()的時候繼續嘗試擷取連接配接。如果設為true,那麼在嘗試擷取連接配接失敗後該資料源将申明已斷開并永久關閉。Default:
false -->
<property name="breakAfterAcquireFailure" value="false"></property>
</bean>
<bean id="dataSourceTargetB" class="com.mchange.v2.c3p0.ComboPooledDataSource"
destroy-method="close" scope="singleton">
<property name="driverClass" value="${jdbc.b.driverClassName}" />
<property name="jdbcUrl" value="${jdbc.b.url}" />
<property name="user" value="${jdbc.b.username}" />
<property name="password" value="${jdbc.b.password}" />
<!--當連接配接池中的連接配接耗盡的時候c3p0一次同時擷取的連接配接數。Default: 3 -->
<property name="acquireIncrement" value="${c3p0.acquireIncrement}"></property>
<!--初始化時擷取三個連接配接,取值應在minPoolSize與maxPoolSize之間。Default: 3 -->
<property name="initialPoolSize" value="${c3p0.initialPoolSize}"></property>
<!--最大空閑時間,60秒内未使用則連接配接被丢棄。若為0則永不丢棄。Default: 0 -->
<property name="maxIdleTime" value="${c3p0.maxIdleTime}"></property>
<!--連接配接池中保留的最大連接配接數。Default: 15 -->
<property name="maxPoolSize" value="${c3p0.maxPoolSize}"></property>
<property name="minPoolSize" value="${c3p0.minPoolSize}"></property>
<!--兩次連接配接中間隔時間,機關毫秒。Default: 1000 -->
<property name="acquireRetryDelay" value="1000"></property>
<!--定義在從資料庫擷取新連接配接失敗後重複嘗試的次數。Default: 30 -->
<property name="acquireRetryAttempts" value="60"></property>
<!--擷取連接配接失敗将會引起所有等待連接配接池來擷取連接配接的線程抛出異常。但是資料源仍有效保留,并在下次調用getConnection()的時候繼續嘗試擷取連接配接。如果設為true,那麼在嘗試擷取連接配接失敗後該資料源将申明已斷開并永久關閉。Default:
false -->
<property name="breakAfterAcquireFailure" value="false"></property>
</bean>
<!-- 動态資料源 -->
<bean id="dynamicDataSource" class="com.test.util.DynamicDataSource" >
<property name="targetDataSources">
<map key-type="java.lang.String">
<entry value-ref="dataSourceTargetA" key="dataSourceTargetA"></entry>
<entry value-ref="dataSourceTargetB" key="dataSourceTargetB"></entry>
</map>
</property>
<property name="defaultTargetDataSource" ref="dataSourceTargetA" >
</property>
</bean>
<bean id="txManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dynamicDataSource" />
</bean>
<!-- MyBatis ORM operation class -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dynamicDataSource" />
<property name="mapperLocations">
<list>
<value>classpath*:com/test/**/*Mapper.xml</value>
</list>
</property>
<property name="configLocation" value="classpath:provider-sql-map-config.xml" />
</bean>