最近做項目需要用到另一個數據庫的內容,多方查找終於實現了功能。
我們都知道,在SSM框架中,我們在applicationContext.xml配置文件中添加數據源就可以實現數據庫增刪改查,但是只能連接一個數據庫,這個時候我們就要從spring提供的源碼下手看看有沒有有關數據源切換的方法,找到關鍵源碼(AbstractRoutingDataSource類,該類就相當於一個dataSource的調度者,用於根據key值來進行切換對應的dataSource。
AbstractRoutingDataSource類:
@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我們可以用過配置文件進行配置,這樣就可以設置當前使用哪個數據庫了,但是需要先要准備一下前提條件。
第一步:我們先要重寫上面的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(); } }
到這里我們的前期的准備工作已經做完了,下面將我的配置文件粘貼出來:
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(); }
到這里兩個數據庫的切換已經可以實現了。