SpringMVC+Mybatis 如何配置多個數據源並切換?


最近公司一個項目需要連接兩個數據庫(A和B)操作,有的模塊查詢A庫,有的模塊查詢B庫,因此需要改造下,項目后台用的是SpringMVC+Mybatis+MySQL架構,折騰了兩天后終於搞定了,在這里記錄過改造過程。

使用場景

多數據源的使用的場景一般有:

  1. 主從數據庫切換
  2. 讀寫分離
  3. 兼容舊庫

實現原理

Spring2.x的版本中采用Proxy模式,就是在方案中實現一個虛擬的數據源,並且用它來封裝數據源選擇邏輯,這樣就可以有效地將數據源選擇邏輯從Client中分離出來。Client提供選擇所需的上下文,由虛擬的DynamicDataSource根據Client提供的上下文來實現數據源的選擇。

具體的實現是虛擬的DynamicDataSource僅需繼承AbstractRoutingDataSource實現determineCurrentLookupKey(),該方法返回需要使用的DataSource的key值,然后根據這個key從resolvedDataSources這個map里取出對應的DataSource,如果找不到則用默認的resolvedDefaultDataSource。

詳細實現過程

  1. 修改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>

  

  2.添加動態數據源管理類

 

DynamicDataSource類繼承AbstractRoutingDataSource,實現determineCurrentLookupKey()方法。

package com.test.util;
import com.test.util.CustomerContextHolder;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

/**
 * 動態數據源管理類
 *
 */
public class DynamicDataSource extends AbstractRoutingDataSource {

    @Override
    protected Object determineCurrentLookupKey() {
        return CustomerContextHolder.getCustomerType();
    }
}

  3. 數據源切換類

CustomerContextHolder類可以實現數據源A和B的切換,數據源標識保存在線程變量中,避免多線程操作數據源時互相干擾。

package com.test.util;
/**
 * 數據源切換類
 *
 */
public class CustomerContextHolder {
    public static final String DATA_SOURCE_DEFAULT = "dataSourceTargetA";
    public static final String DATA_SOURCE_B = "dataSourceTargetB";
    private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>();
    public static void setCustomerType(String customerType) {
        contextHolder.set(customerType);
    }
    public static String getCustomerType() {
        return contextHolder.get();
    }
    public static void clearCustomerType() {
        contextHolder.remove();
    }
}

 4. 使用多數據源代碼

下面是Controller層代碼,需要查詢B數據源,查詢完成后切換為默認數據源A,具體代碼如下:

/**
     * 訂單列表查詢,查詢B數據源
     *
     * @param request
     * @return
     */
    @RequestMapping(value = "/list")
    public String list(HttpServletRequest request, Integer cId) {
        //設置數據源B
        CustomerContextHolder.setCustomerType(CustomerContextHolder.DATA_SOURCE_B);
        List<Order> orderList = orderService.getListByCid(cId);
        request.setAttribute("orderList", orderList);
        //切換回默認數據源A
        CustomerContextHolder.setCustomerType(CustomerContextHolder.DATA_SOURCE_DEFAULT);
        return "/jsp/order";
    }

 

上述四步操作完成后即可實現SpringMVC+Mybatis配置多個數據源並切換,有問題歡迎留言溝通哦!

推薦閱讀

1.Spring Boot 2.X 如何優雅的解決跨域問題?
2.Redis Cluster搭建高可用Redis服務器集群
3.為什么單線程的Redis這么快?
4.Spring Boot集成spring session實現session共享
5.Spring Boot入門-快速搭建web項目
6.Spring Boot2.0整合Redis
7.一篇文章搞定SpringMVC參數綁定

 

限時領取免費Java相關資料,涵蓋了Java、Redis、MongoDB、MySQL、Zookeeper、Spring Cloud、Dubbo/Kafka、Hadoop、Hbase、Flink等高並發分布式、大數據、機器學習等技術。

資料傳送門:  https://mp.weixin.qq.com/s/u2b_NVNuMuAPE0w4lc45fw

關注下方公眾號即可免費領取:

Java碎碎念公眾號


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM