Spring+Mybatis 實(shí)現(xiàn)aop數(shù)據(jù)庫(kù)讀寫(xiě)分離與多數(shù)據(jù)庫(kù)源配置操作
在數(shù)據(jù)庫(kù)層面大都采用讀寫(xiě)分離技術(shù),就是一個(gè)Master數(shù)據(jù)庫(kù),多個(gè)Slave數(shù)據(jù)庫(kù)。Master庫(kù)負(fù)責(zé)數(shù)據(jù)更新和實(shí)時(shí)數(shù)據(jù)查詢(xún),Slave庫(kù)當(dāng)然負(fù)責(zé)非實(shí)時(shí)數(shù)據(jù)查詢(xún)。因?yàn)樵趯?shí)際的應(yīng)用中,數(shù)據(jù)庫(kù)都是讀多寫(xiě)少(讀取數(shù)據(jù)的頻率高,更新數(shù)據(jù)的頻率相對(duì)較少),而讀取數(shù)據(jù)通常耗時(shí)比較長(zhǎng),占用數(shù)據(jù)庫(kù)服務(wù)器的CPU較多,從而影響用戶(hù)體驗(yàn)。我們通常的做法就是把查詢(xún)從主庫(kù)中抽取出來(lái),采用多個(gè)從庫(kù),使用負(fù)載均衡,減輕每個(gè)從庫(kù)的查詢(xún)壓力。
廢話(huà)不多說(shuō),多數(shù)據(jù)源配置和主從數(shù)據(jù)配置原理一樣
1、首先配置 jdbc.properties 兩個(gè)數(shù)據(jù)庫(kù) A 和 B
#============ 雙數(shù)據(jù)源 ======# #----------------------A servers--------------------------# A.driver=com.mysql.jdbc.Driver A.url=jdbc:mysql://localhost:3619/gps4?useUnicode=true&characterEncoding=utf8 A.username=gpsadmin A.password=1qaz&619 #----------------------B servers--------------------------# B.driver=com.mysql.jdbc.Driver B.url=jdbc:mysql://localhost:3619/gps6?useUnicode=true&characterEncoding=utf8 B.username=gpsadmin B.password=1qaz&619
2、配置 spring-mybatis.xml 文件【重要】
<!-- 引入配置文件 -->
<bean id="propertyConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="classpath:resources/jdbc.properties" />
</bean>
<!-- DBCP連接池 -->
<!-- <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close"> <property name="driverClassName" value="${driver}"
/> <property name="url" value="${url}" /> <property name="username" value="${username}"
/> <property name="password" value="${password}" /> 初始化連接大小 <property name="initialSize"
value="${initialSize}"></property> 連接池最大數(shù)量 <property name="maxActive" value="${maxActive}"></property>
連接池最大空閑 <property name="maxIdle" value="${maxIdle}"></property> 連接池最小空閑 <property
name="minIdle" value="${minIdle}"></property> 獲取連接最大等待時(shí)間 <property name="maxWait"
value="${maxWait}"></property> </``> -->
<!-- 【重點(diǎn)】 A 數(shù)據(jù)源 -->
<bean name="dataSourceA" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${A.driver}" />
<property name="url" value="${A.url}" />
<property name="username" value="${A.username}" />
<property name="password" value="${A.password}" />
</bean>
<!-- 【重點(diǎn)】 B 數(shù)據(jù)源 -->
<bean name="dataSourceB" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${B.driver}" />
<property name="url" value="${B.url}" />
<property name="username" value="${B.username}" />
<property name="password" value="${B.password}" />
</bean>
<!--【重點(diǎn)】 雙數(shù)據(jù)源 配合 -->
<bean id="dataSource" class="com.ifengSearch.common.database.DynamicDataSource">
<property name="defaultTargetDataSource" ref="dataSourceB"/>
<property name="targetDataSources">
<map>
<entry key="dataSourceA" value-ref="dataSourceA"/>
<entry key="dataSourceB" value-ref="dataSourceB"/>
</map>
</property>
</bean>
<!-- 【重點(diǎn)】 加入 aop 自動(dòng)掃描 DataSourceAspect 配置數(shù)據(jù)庫(kù)注解aop -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
<bean id="manyDataSourceAspect" class="com.ifengSearch.common.database.DataSourceAspect" />
<aop:config>
<!-- 掃描 注解的 數(shù)據(jù)源 -->
<aop:aspect id="c" ref="manyDataSourceAspect">
<aop:pointcut id="tx" expression="execution(* com.ifengSearch.*.dao.*.*(..))"/>
<aop:before pointcut-ref="tx" method="before"/>
</aop:aspect>
</aop:config>
<!-- 配置數(shù)據(jù)連接 工廠(chǎng) 自動(dòng)掃描mapping.xml文件 -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<!-- 自動(dòng)掃描mapping.xml文件 -->
<property name="mapperLocations" value="classpath:com/ifengSearch/*/mapping/*.xml"></property>
</bean>
<!-- DAO接口所在包名,Spring會(huì)自動(dòng)查找其下的類(lèi) -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.ifengSearch.*.dao" />
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
</bean>
<!-- (事務(wù)管理)transaction manager, use JtaTransactionManager for global tx 事務(wù) -->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
3、編寫(xiě)幾個(gè)java類(lèi)動(dòng)態(tài)調(diào)用數(shù)據(jù)源【重要】
a:自定義一個(gè)注解,負(fù)責(zé)動(dòng)態(tài)調(diào)用數(shù)據(jù)源
package com.ifengSearch.common.database;
import java.lang.annotation.*;
/**
* 設(shè)置 數(shù)據(jù)源 注解標(biāo)簽的用法 寫(xiě)上注解標(biāo)簽,
* 調(diào)用相應(yīng)方法切換數(shù)據(jù)源咯(就跟你設(shè)置事務(wù)一樣)
* 【也可以配置 主從數(shù)據(jù)庫(kù)】
*
* @author flm
* @2017年9月12日
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface DataSource {
public static String dataSourceA = "dataSourceA"; // A數(shù)據(jù)源
public static String dataSourceB = "dataSourceB"; // B數(shù)據(jù)源
String value();
}
b、數(shù)據(jù)源的獲取 Object aop實(shí)現(xiàn) (反射)
package com.ifengSearch.common.database;
import java.lang.reflect.Method;
import org.apache.log4j.Logger;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
/**
* 數(shù)據(jù)源的獲取
* aop實(shí)現(xiàn) (反射)
* @author flm
* @2017年9月12日
*/
public class DataSourceAspect{
private Logger log = Logger.getLogger(DataSourceAspect.class);
public void before(JoinPoint point)
{
Object target = point.getTarget();// 攔截的實(shí)體類(lèi)
String method = point.getSignature().getName();// 攔截的方法名稱(chēng)
Class<?>[] classz = target.getClass().getInterfaces();
Class<?>[] parameterTypes = ((MethodSignature) point.getSignature())
.getMethod().getParameterTypes();// 攔截的方法參數(shù)類(lèi)型
try {
Method m = classz[0].getMethod(method, parameterTypes);
if (m != null && m.isAnnotationPresent(DataSource.class)) {
DataSource data = m
.getAnnotation(DataSource.class);
DataSourceHolder.setDataSource(data.value());
log.info("數(shù)據(jù)源的獲取 DataSource: "+data.value());
}
} catch (Exception e) {
log.error("數(shù)據(jù)源的獲取 aop實(shí)現(xiàn) 出錯(cuò):"+e.getMessage());
}
}
}
c、DataSourceHolder 數(shù)據(jù)源操作 獲取數(shù)據(jù)源 幫助類(lèi)
package com.ifengSearch.common.database;
/**
* 多數(shù)據(jù)源
* 數(shù)據(jù)源操作 獲取數(shù)據(jù)源
* @author flm
* @2017年9月12日
*/
public class DataSourceHolder {
//線(xiàn)程本地環(huán)境
private static final ThreadLocal<String> dataSources = new ThreadLocal<String>();
//設(shè)置數(shù)據(jù)源
public static void setDataSource(String customerType) {
dataSources.set(customerType);
}
//獲取數(shù)據(jù)源
public static String getDataSource() {
return (String) dataSources.get();
}
//清除數(shù)據(jù)源
public static void clearDataSource() {
dataSources.remove();
}
}
d、 我們還需要實(shí)現(xiàn)spring的抽象類(lèi)AbstractRoutingDataSource,就是實(shí)現(xiàn)determineCurrentLookupKey方法:
package com.ifengSearch.common.database;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
/**
* 多數(shù)據(jù)源
* 獲取數(shù)據(jù)源(依賴(lài)于spring)
* @author flm
* @2017年9月12日
*/
public class DynamicDataSource extends AbstractRoutingDataSource{
@Override
protected Object determineCurrentLookupKey() {
return DataSourceHolder.getDataSource();
}
}
4、接下來(lái)就可以看結(jié)果了
我在dao層直接調(diào)用
public interface UserDao {
/**
* 登錄判斷 【數(shù)據(jù)源B】
*/
@DataSource(value=DataSource.dataSourceB)
public List<UserBean> getLoginUserList(@Param("loginName")String loginName,@Param("loginPwd")String loginPwd);
/**
* 查找上一級(jí) 服務(wù)商 【數(shù)據(jù)源A】
*/
@DataSource(value=DataSource.dataSourceA)
public UserBean getServerUser(@Param("u_last_id")Integer u_last_id);
}
總結(jié)
以上所述是小編給大家介紹的Spring+Mybatis 實(shí)現(xiàn)aop數(shù)據(jù)庫(kù)讀寫(xiě)分離與多數(shù)據(jù)庫(kù)源配置操作,希望對(duì)大家有所幫助,如果大家有任何疑問(wèn)請(qǐng)給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對(duì)腳本之家網(wǎng)站的支持!
相關(guān)文章
springboot項(xiàng)目中idea的pom.xml文件的引用標(biāo)簽全部爆紅問(wèn)題解決
這篇文章主要介紹了springboot項(xiàng)目中idea的pom.xml文件的引用標(biāo)簽全部爆紅問(wèn)題解決,本文通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),需要的朋友參考下吧2023-12-12
Idea 同一窗口導(dǎo)入多個(gè)項(xiàng)目的實(shí)現(xiàn)步驟
本文主要介紹了Idea 同一窗口導(dǎo)入多個(gè)項(xiàng)目的實(shí)現(xiàn)步驟,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-07-07
Java中的break和continue關(guān)鍵字的使用方法總結(jié)
下面小編就為大家?guī)?lái)一篇Java中的break和continue關(guān)鍵字的使用方法總結(jié)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2016-11-11
java 實(shí)現(xiàn) stack詳解及實(shí)例代碼
這篇文章主要介紹了java 實(shí)現(xiàn) stack詳解的相關(guān)資料,需要的朋友可以參考下2016-09-09
Java使用JDBC向MySQL數(shù)據(jù)庫(kù)批次插入10W條數(shù)據(jù)(測(cè)試效率)
使用JDBC連接MySQL數(shù)據(jù)庫(kù)進(jìn)行數(shù)據(jù)插入的時(shí)候,特別是大批量數(shù)據(jù)連續(xù)插入(100000),如何提高效率呢?今天小編通過(guò)本教程給大家介紹下2016-12-12
基于SpringBoot解決CORS跨域的問(wèn)題(@CrossOrigin)
這篇文章主要介紹了基于SpringBoot解決CORS跨域的問(wèn)題(@CrossOrigin),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2021-01-01
解決SpringBoot中LocalDateTime返回前端數(shù)據(jù)為數(shù)組結(jié)構(gòu)的問(wèn)題
本文主要介紹了解決SpringBoot中LocalDateTime返回前端數(shù)據(jù)為數(shù)組結(jié)構(gòu)的問(wèn)題,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2025-03-03
Java httpClient連接池支持多線(xiàn)程高并發(fā)的實(shí)現(xiàn)
本文主要介紹了Java httpClient連接池支持多線(xiàn)程高并發(fā)的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-08-08
詳解hibernate雙向多對(duì)多關(guān)聯(lián)映射XML與注解版
本篇文章主要介紹了詳解hibernate雙向多對(duì)多關(guān)聯(lián)映射XML與注解版,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-05-05

