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();
}
到這裡兩個資料庫的切換已經可以實現了。