Spring實現多資料來源配置
阿新 • • 發佈:2018-12-16
一、前言
對於小型專案,伺服器與資料庫是可以在同一臺機子上的,但隨著業務的龐大與負責,資料庫和伺服器就會分離開來。同時隨著資料量的增大,資料庫也要分開部署到多臺機子上。
二、Spring配置檔案修改
在理論學習與實踐的差距:框架開源與不可逆的趨勢[1]一文中曾經介紹過SSM框架,當時的框架採取單一資料來源的配置,同時資料庫的地址也沒有寫在properties檔案中。但實際開發中,如果需要更換伺服器的地址,修改XML檔案會比較麻煩,一般都是提倡用properties檔案,部署到正式伺服器上,就只需要修改properties檔案即可。
1、applicationContext.properties檔案:
#Mysql jdbc.mysql.driver=com.mysql.jdbc.Driver jdbc.mysql.url=jdbc:mysql://localhost:3306/db_test jdbc.mysql.username=root jdbc.mysql.password=123456 #Mysql2 jdbc.mysql.driver2=com.mysql.jdbc.Driver jdbc.mysql.url2=jdbc:mysql://192.168.1.8:3306/db_test jdbc.mysql.username2=root jdbc.mysql.password2=123456
2、動態資料來源配置 [2]
package com.test.utils; import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; public class DynamicDataSource extends AbstractRoutingDataSource{ public static final String mySqlDataSource = "mySqlDataSource"; public static final String mySqlDataSource2 = "mySqlDataSource2"; //本地執行緒,獲取當前正在執行的currentThread public 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(); } @Override protected Object determineCurrentLookupKey() { // TODO Auto-generated method stub return getCustomerType(); } }
3、applicationContext.xml檔案:
添加了對配置檔案applicationContext.properties的讀取,同時在dynamicDataSource中新增兩個或以上的資料來源,並定義一個預設的資料來源
<?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:p="http://www.springframework.org/schema/p"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:jee="http://www.springframework.org/schema/jee"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-4.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd">
<!-- 自動掃描 -->
<context:component-scan base-package="com.test.dao" />
<context:component-scan base-package="com.test.service" />
<!-- 讀取配置檔案 -->
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:applicationContext.properties</value>
<!-- <value>classpath:config.properties</value> -->
</list>
</property>
<property name="ignoreUnresolvablePlaceholders" value="true"/>
</bean>
<!-- 配置資料來源 -->
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${jdbc.mysql.driver}"/>
<property name="url" value="${jdbc.mysql.url}"/>
<property name="username" value="${jdbc.mysql.username}"/>
<property name="password" value="${jdbc.mysql.password}"/>
</bean>
<bean id="dataSource2"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${jdbc.mysql.driver2}"/>
<property name="url" value="${jdbc.mysql.url2}"/>
<property name="username" value="${jdbc.mysql.username2}"/>
<property name="password" value="${jdbc.mysql.password2}"/>
</bean>
<!-- 動態資料來源 -->
<bean id="dynamicDataSource" class="com.test.utils.DynamicDataSource"><!--注意: 這裡寫選擇資料來源的類地址 下面跟著給出 -->
<property name="targetDataSources">
<map>
<entry key="mySqlDataSource" value-ref="dataSource" />
<entry key="mySqlDataSource2" value-ref="dataSource2" />
</map>
</property>
<property name="defaultTargetDataSource" ref="dataSource" /><!-- 設定預設為此mySqlDataSource資料來源 -->
</bean>
<!-- 配置mybatis的sqlSessionFactory -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dynamicDataSource" />
<!-- 自動掃描mappers.xml檔案 -->
<property name="mapperLocations" value="classpath:com/test/mappers/*.xml"></property>
<!-- mybatis配置檔案 -->
<property name="configLocation" value="classpath:mybatis-config.xml"></property>
</bean>
<!-- DAO介面所在包名,Spring會自動查詢其下的類 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.test.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>
<!-- 配置事務通知屬性 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!-- 定義事務傳播屬性 -->
<tx:attributes>
<tx:method name="insert*" propagation="REQUIRED" />
<tx:method name="update*" propagation="REQUIRED" />
<tx:method name="edit*" propagation="REQUIRED" />
<tx:method name="save*" propagation="REQUIRED" />
<tx:method name="add*" propagation="REQUIRED" />
<tx:method name="new*" propagation="REQUIRED" />
<tx:method name="set*" propagation="REQUIRED" />
<tx:method name="remove*" propagation="REQUIRED" />
<tx:method name="delete*" propagation="REQUIRED" />
<tx:method name="change*" propagation="REQUIRED" />
<tx:method name="get*" propagation="REQUIRED" read-only="true" />
<tx:method name="find*" propagation="REQUIRED" read-only="true" />
<tx:method name="load*" propagation="REQUIRED" read-only="true" />
<tx:method name="*" propagation="REQUIRED" read-only="false" />
</tx:attributes>
</tx:advice>
<!-- 配置事務切面 -->
<aop:config>
<aop:pointcut id="serviceOperation"
expression="execution(* com.test.service.*.*(..))" />
<aop:advisor advice-ref="txAdvice" pointcut-ref="serviceOperation" />
</aop:config>
</beans>
三、實際執行程式碼並動態切換資料來源
1、前臺jsp頁面:
這裡簡單用a標籤同步請求獲取資料
<a href="${pageContext.request.contextPath }/blog/searchAllBlog.do">獲取列表</a>
<table>
<c:choose>
<c:when test="${blogList==null }">
<tr>
<td>暫無列表</td>
</tr>
</c:when>
<c:otherwise>
<tr>
<th> </th>
<th>標題</th>
<th>內容</th>
</tr>
<c:forEach var="blog" items="${blogList }" varStatus="status">
<tr>
<td>${status.index+1 }</td>
<td>${blog.title }</td>
<td>${blog.content }</td>
</tr>
</c:forEach>
</c:otherwise>
</c:choose>
</table>
<hr>
<a href="${pageContext.request.contextPath }/blog/searchAllBlog2.do">獲取列表2</a>
<table>
<c:choose>
<c:when test="${blogList2==null }">
<tr>
<td>暫無列表</td>
</tr>
</c:when>
<c:otherwise>
<tr>
<th> </th>
<th>標題</th>
<th>內容</th>
</tr>
<c:forEach var="blog" items="${blogList2 }" varStatus="status">
<tr>
<td>${status.index+1 }</td>
<td>${blog.title }</td>
<td>${blog.content }</td>
</tr>
</c:forEach>
</c:otherwise>
</c:choose>
</table>
2、Controller層
@RequestMapping("/searchAllBlog")
public String searchAllBlog(HttpServletRequest request){
List<Blog> blogList=blogService.searchAllBlog();
request.setAttribute("blogList", blogList);
return "add";
}
@RequestMapping("/searchAllBlog2")
public String searchAllBlog2(HttpServletRequest request){
List<Blog> blogList=blogService.searchAllBlog2();
request.setAttribute("blogList2", blogList);
return "add";
}
3、Service層
public interface BlogService {
public int add(Blog blog);
public List<Blog> searchAllBlog();
public List<Blog> searchAllBlog2();
}
對應的實現類:[2]
@Override
public List<Blog> searchAllBlog() {
// TODO Auto-generated method stub
return blogDao.searchAllBlog();
}
@Override
public List<Blog> searchAllBlog2() {
// TODO Auto-generated method stub
List<Blog> list = null;
DynamicDataSource.clearCustomerType();//重點: 實際操作證明,切換的時候最好清空一下
DynamicDataSource.setCustomerType(DynamicDataSource.mySqlDataSource2);
list = blogDao.searchAllBlog();
DynamicDataSource.clearCustomerType();//
DynamicDataSource.setCustomerType(DynamicDataSource.mySqlDataSource);//切換回主資料來源
return list;
}
四、執行程式
執行程式後,點選獲取獲取列表2,會發現不允許連線伺服器。原因是由於Mysql預設只能本機訪問,不能夠遠端連線。這裡需要對MySql進行開啟許可權操作。[4]
grant all on 資料庫名.* to ‘資料庫賬戶名’@’%’ identified by ‘密碼’ with grant option;
回車
flush privileges;
回車
注意:上面的單引號不能省,資料庫名.* 表示要開放的資料庫下所有表,如果該連線的所有資料庫都要開放,可以用 *.* 代替。
‘資料庫賬戶名’@’%’ 這裡表示要開放的賬戶,百分號表示在任何主機都允許訪問。
如果以上兩步均顯示 “Query OK, 0 rows affected (0.00 sec)”,那麼說明命令已經成功執行,現在就可以遠端連線你的mysql資料庫了。
五、總結
隨著業務的複雜增加,一般都會採取多資料來源的方式,減輕伺服器壓力。一般來說Mysql佔用系統資源比較少,對伺服器效能影響不大,如果需要多種不同型別的資料庫,那麼分別部署到不同的伺服器上可以優化效能,提供系統的執行速度。當然,如果專案過於巨大,會被變成分散式專案,將不同功能的子系統部署到不同伺服器上。