1. 程式人生 > >SSM專案實現連線兩個mysql資料庫

SSM專案實現連線兩個mysql資料庫

最近做專案需要用到另一個數據庫的內容,多方查詢終於實現了功能。

我們都知道,在SSM框架中,我們在applicationContext.xml配置檔案中新增資料來源就可以實現資料庫增刪改查,但是隻能連線一個數據庫,這個時候我們就要從spring提供的原始碼下手看看有沒有有關資料來源切換的方法,找到關鍵原始碼(AbstractRoutingDataSource類,該類就相當於一個dataSource的排程者,用於根據key值來進行切換對應的dataSource。):

@Override
    public Connection getConnection() throws SQLException {
        return determineTargetDataSource().getConnection();
    }

    @Override
    public Connection getConnection(String username, String password) throws SQLException {
        return determineTargetDataSource().getConnection(username, password);
    }

    /**
     * Retrieve the current target DataSource. Determines the
     * {@link #determineCurrentLookupKey() current lookup key}, performs
     * a lookup in the {@link #setTargetDataSources targetDataSources} map,
     * falls back to the specified
     * {@link #setDefaultTargetDataSource default target DataSource} if necessary.
     * @see #determineCurrentLookupKey()
     */
    protected DataSource determineTargetDataSource() {
        Assert.notNull(this.resolvedDataSources, "DataSource router not initialized");
        Object lookupKey = determineCurrentLookupKey();
        DataSource dataSource = this.resolvedDataSources.get(lookupKey);
        if (dataSource == null && (this.lenientFallback || lookupKey == null)) {
            dataSource = this.resolvedDefaultDataSource;
        }
        if (dataSource == null) {
            throw new IllegalStateException("Cannot determine target DataSource for lookup key [" + lookupKey + "]");
        }
        return dataSource;
    }

    /**
     * Determine the current lookup key. This will typically be
     * implemented to check a thread-bound transaction context.
     * <p>Allows for arbitrary keys. The returned key needs
     * to match the stored lookup key type, as resolved by the
     * {@link #resolveSpecifiedLookupKey} method.
     */
    protected abstract Object determineCurrentLookupKey();

可以看出方法getConnection()呼叫的determineTargetDataSource則是關鍵方法,這個方法返回了具體使用的是哪個資料庫;而

determineCurrentLookupKey()方法來返回當前資料來源的key值。

將返回的key值在resolvedDataSources這個map中找到對應的value(當前使用的資料來源)。 原始碼:

@Override
    public void afterPropertiesSet() {
        if (this.targetDataSources == null) {
            throw new IllegalArgumentException("Property 'targetDataSources' is required");
        }
        this.resolvedDataSources = new HashMap<Object, DataSource>(this.targetDataSources.size());
        for (Map.Entry<Object, Object> entry : this.targetDataSources.entrySet()) {
            Object lookupKey = resolveSpecifiedLookupKey(entry.getKey());
            DataSource dataSource = resolveSpecifiedDataSource(entry.getValue());
            this.resolvedDataSources.put(lookupKey, dataSource);
        }
        if (this.defaultTargetDataSource != null) {
            this.resolvedDefaultDataSource = resolveSpecifiedDataSource(this.defaultTargetDataSource);
        }
    }

這個方法是通過targetDataSources對resolvedDataSources進行賦值的。targetDataSources我們可以用過配置檔案進行配置,這樣就可以設定當前使用哪個資料庫了,但是需要先要準備一下前提條件。

1.我們先要重寫上面的determineCurrentLookupKey方法,我們新建一個建立一個DynamicDataSource的類,用來獲取自定義獲取資料來源的標識:

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

public class DynamicDataSource extends AbstractRoutingDataSource {

    @Override
    protected Object determineCurrentLookupKey() {
        // 從自定義的位置獲取資料來源標識
        return DynamicDataSourceHolder.getDataSource();
    }

}

第二步:建立DynamicDataSourceHolder類用於切換要操作的資料來源,程式碼如下:

package com.dingdao.apiserver.utils;

public class DynamicDataSourceHolder
{
  private static final ThreadLocal<String> THREAD_DATA_SOURCE = new ThreadLocal();
  
  public static String getDataSource()
  {
    return (String)THREAD_DATA_SOURCE.get();
  }
  
  public static void setDataSource(String dataSource)
  {
    THREAD_DATA_SOURCE.set(dataSource);
  }
  
  public static void clearDataSource()
  {
    THREAD_DATA_SOURCE.remove();
  }
}

第三步:建立DynamicDataSource.java類用於獲取當前執行緒中使用的資料來源,程式碼如下:

package com.dingdao.apiserver.utils;

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

public class DynamicDataSource
  extends AbstractRoutingDataSource
{
  protected Object determineCurrentLookupKey()
  {
    return DynamicDataSourceHolder.getDataSource();
  }
}

到這裡我們的前期的準備工作已經做完了,下面將我的配置檔案粘貼出來:

jdbc.properties

driverClasss =com.mysql.jdbc.Driver(筆者用的mysql,如果不是mysql請自行替換)
jdbcUrl=jdbc:第一個資料庫的連結
username=第一個資料庫的使用者名稱
password=第一個資料庫的密碼
jrt_driverClasss=com.mysql.jdbc.Driver(筆可以直接呼叫第一個的driverClasss,筆者只是用於看著舒服,啊哈哈)
jrt_jdbcUrl=jdbc:第二個資料庫的連結
jrt_username=第二個資料庫的使用者名稱
jrt_password=第二個資料庫的密碼
#定義初始連線數
initialSize=0
#定義最大連線數
maxActive=20
#定義最大空閒
maxIdle=20
#定義最小空閒
minIdle=1
#定義最長等待時間
maxWait=60000

spring-mybatis.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:tx="http://www.springframework.org/schema/tx"
       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/tx
                        http://www.springframework.org/schema/tx/spring-tx.xsd">

    <!-- 自動掃描 -->
    <context:component-scan base-package="com.dingdao.apiserver.*"/>

    <!-- 第一種方式:載入一個properties檔案 -->
    <bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="location" value="classpath:jdbc.properties"/>
    </bean>


    

    <!-- 配置第一個資料來源 -->
    <bean id="defultdataSource" class="org.apache.commons.dbcp.BasicDataSource"
          destroy-method="close">
        <property name="driverClassName" value="${driverClasss}"/>
        <property name="url" value="${jdbcUrl}"/>
        <property name="username" value="${username}"/>
        <property name="password" value="${password}"/>
        <!-- 初始化連線大小 -->
        <property name="initialSize" value="${initialSize}"></property>
        <!-- 連線池最大數量 -->
        <property name="maxActive" value="${maxActive}"></property>
        <!-- 連線池最大空閒 -->
        <property name="maxIdle" value="${maxIdle}"></property>
        <!-- 連線池最小空閒 -->
        <property name="minIdle" value="${minIdle}"></property>
        <!-- 獲取連線最大等待時間 -->
        <property name="maxWait" value="${maxWait}"></property>
    </bean>

    <!-- 配置第二個資料來源 -->
    <bean id="jrt_dataSource" class="org.apache.commons.dbcp.BasicDataSource"
          destroy-method="close">
        <property name="driverClassName" value="${jrt_driverClasss}"/>
        <property name="url" value="${jrt_jdbcUrl}"/>
        <property name="username" value="${jrt_username}"/>
        <property name="password" value="${jrt_password}"/>
        <!-- 初始化連線大小 -->
        <property name="initialSize" value="${initialSize}"></property>
        <!-- 連線池最大數量 -->
        <property name="maxActive" value="${maxActive}"></property>
        <!-- 連線池最大空閒 -->
        <property name="maxIdle" value="${maxIdle}"></property>
        <!-- 連線池最小空閒 -->
        <property name="minIdle" value="${minIdle}"></property>
        <!-- 獲取連線最大等待時間 -->
        <property name="maxWait" value="${maxWait}"></property>
    </bean>


    <bean id="dataSource" class="com.dingdao.apiserver.utils.DynamicDataSource">
        <property name="targetDataSources">
            <map key-type="java.lang.String">
                <!-- 指定lookupKey和與之對應的資料來源,這裡的key可以自行定義,要切換資料庫的時候以key為標識,不要寫錯 -->
                <entry key="defultdataSource" value-ref="defultdataSource"></entry>
                <entry key="jrt_dataSource" value-ref="jrt_dataSource"></entry>
            </map>
        </property>
        <!-- 這裡可以指定預設的資料來源 -->
        <property name="defaultTargetDataSource" ref="defultdataSource" />
    </bean>



    <!-- mybatis和spring完美整合,不需要mybatis的配置對映檔案 -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <!-- 自動掃描mapping.xml檔案 -->
        <property name="mapperLocations" value="classpath:mapper/*.xml"></property>
    </bean>

    <!-- DAO介面所在包名,Spring會自動查詢其下的類 -->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.dingdao.apiserver.dao"/>
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
    </bean>


    <!-- (事務管理)transaction manager, use JtaTransactionManager for global tx -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <!-- (事務管理)transaction manager, use JtaTransactionManager for global tx -->
    <tx:annotation-driven transaction-manager="transactionManager"/>


</beans>

使用預設資料庫的時候可以什麼都不用寫,但是切換為非預設資料庫的時候就要進行設定了,下面把呼叫兩種資料庫的方法貼出來:

使用預設資料庫的service的實現類程式碼:

 @Autowired
    @Qualifier("nitceDao")
    NitceDao nitceDao;

    public Map<String, Object> getNotice(Map<String, Object> map) {
        List<Map<String, Object>> notice = nitceDao.getNotice(map);
        Map<String,Object> maps = new HashMap<String, Object>();
        maps.put("lists", notice);
        return maps;
    }

不適用預設資料庫service的實現類程式碼:

 @Autowired
    @Qualifier("testJRTDao")
    private TestJRTDao testJRTDao;

    public String AllColor() {
        DynamicDataSourceHolder.setDataSource("jrt_dataSource");
        JSONObject jsonObject = new JSONObject();
        List<Map<String ,Object>> list = testJRTDao.selectAllColor();
        jsonObject.put("AllColor",list);
        return jsonObject.toString();
    }


到這裡兩個資料庫的切換已經可以實現了。