spring --讀寫分離

實現流程java

1.ssm基本流程
1.1 一個http請求,被springMVC的DispatcherServlet攔截,而後交給HandleRequest處理。
1.2 HandleRequest調用HandlerMapping與HandlerAdapter兩個類,去匹配controller上的路徑。
1.3 一旦匹配上,就執行該路徑對應的方法。
1.4 controller層只是起一個控制做用,實際的業務處理交給service實現層。
1.5 實現層處理完數據,將結果返回給controller層。
這是一個基本的ssm流程
2.實現讀寫分離  
2.1 配置一個動態數據源切面,將該切面添加到service上。
2.2 一旦有請求進來,執行到service層,該切面進行攔截,可以獲取到請求方法的名稱和參數。
2.3 這裏咱們只須要方法名稱就夠了,實際上這裏拿到的方法名稱與請求參數能夠作緩存(便是spring緩存原理)。該切面完成的功能是:根據執行方法名稱判斷應該走讀庫仍是走寫庫,這裏返回的是一個標記(讀庫仍是寫庫的標記)。
2.4 接着有一個動態數據源的bean,該bean繼承spring的AbstractRoutingDataSource類,而且覆寫該類的determineCurrentLookupKey方法,返回讀寫庫標記。同時該bean配置在spring-mybatis.xml文件中,因爲該bean繼承自AbstractRoutingDataSource類,該類有個屬性targetDataSources,這個屬性的類型是map,在這個map中設置讀寫標記對應讀寫數據源,根據剛剛傳進來的標記來匹配該map中的key值,根據這個key值走不一樣的數據源。
2.5 spring-mybatis.xml中配置兩套數據源,上述的key值就是走的這兩套數據源中的一個。

本文基於另外一篇文章
http://www.javashuo.com/article/p-sxtutbnw-dq.html
spring讀寫分離其實就是mysql主從複製,只不過添加了動態切換數據源的功能。
參考文章
http://blog.csdn.net/jack85986370/article/details/51559232
這一篇好像更加不錯
http://blog.csdn.net/u013632755/article/details/51557956#comments
###1.完成搭建ssm最小系統
參見我這篇文章 http://www.javashuo.com/article/p-avkcqvwb-em.html
###2.添加master包
輸入圖片說明
###3.DataSourceAspectmysql

package com.test.spring.master;

import org.aspectj.lang.JoinPoint;

public class DataSourceAspect {
	
	/**
	 * 進入service方法以前
	 */
	public void before2(JoinPoint point) {
		String methodName = point.getSignature().getName();
		if (isSlave(methodName)) {
			//走寫庫
			DynamicDataSourceHolder.markMaster();
		}else {
			//走讀庫
			DynamicDataSourceHolder.markSlave();
		}
	}
	
	/**
	 * 判斷是否爲讀庫
	 */
    private Boolean isSlave(String methodName) {  
        // 方法名以query、find、get開頭的方法名走從庫 
    	boolean flag = true;
    	
    	if (methodName.contains("query")||
    		methodName.contains("find") ||
    		methodName.contains("get")) {
    		flag = false;
		}
    	return flag;
    }
}

###4.DynamicDataSourcespring

package com.test.spring.master;

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

public class DynamicDataSource extends AbstractRoutingDataSource{

	@Override
	protected Object determineCurrentLookupKey() {
		System.out.println("----------DynamicDataSource---------");
		return DynamicDataSourceHolder.getDataSourceKey();
	}

}

###5.DynamicDataSourceHoldersql

package com.test.spring.master;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class DynamicDataSourceHolder {
	
	private static final Logger LOGGER = LoggerFactory.getLogger(DynamicDataSourceHolder.class);
	
	//寫庫對應的數據源key
	private static final String MASTER = "master";
	
	//讀庫對應的數據源key 
	private static final String SLAVE = "slave";
	
    //使用ThreadLocal記錄當前線程的數據源key  
    private static final ThreadLocal<String> holder = new ThreadLocal<String>(); 
	
    /** 
     * 設置數據源key 
     * @param key 
     */  
    public static void putDataSourceKey(String key) {
    	if (LOGGER.isDebugEnabled()) {
    		LOGGER.debug("------切換數據源,key:"+key+"-------");
		}
        holder.set(key);  
    }
    
    /** 
     * 獲取數據源key 
     * @return 
     */  
    public static String getDataSourceKey() {  
        return holder.get();  
    }
    
    /** 
     * 標記寫庫 
     */  
    public static void markMaster(){  
        putDataSourceKey(MASTER);  
    }  
      
    /** 
     * 標記讀庫 
     */  
    public static void markSlave(){  
        putDataSourceKey(SLAVE);  
    }
}

###6.db.properties數據庫

driverClassName=com.mysql.jdbc.Driver
#主數據庫
master_jdbc_url=jdbc:mysql://192.168.2.11:3306/test?useUnicode=true&characterEncoding=UTF-8
master_jdbc_username=root
master_jdbc_password=123456

#從數據庫
slave_jdbc_url=jdbc:mysql://192.168.2.11:3307/test?useUnicode=true&characterEncoding=UTF-8
slave_jdbc_username=root
slace_jdbc_password=654321

注意:mysql搭建在同一臺主機上,僅僅端口不一樣
###7.spring-mybatis.xmlexpress

<?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"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.1.xsd
		http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.1.xsd">

	<!-- 讀取配置文件 -->
	<context:property-placeholder location="classpath:db.properties"/>
	
	<!-- 配置master數據源 -->
	<bean id="masterDataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
		<property name="driverClassName" value="${driverClassName}"></property>
		<property name="url" value="${master_jdbc_url}"></property>
		<property name="username" value="${master_jdbc_username}"></property>
		<property name="password" value="${master_jdbc_password}"></property>
	</bean>
	
	<!-- 配置slave數據源 -->
	<bean id="slaveDataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
		<property name="driverClassName" value="${driverClassName}"></property>
		<property name="url" value="${slave_jdbc_url}"></property>
		<property name="username" value="${slave_jdbc_username}"></property>
		<property name="password" value="${slace_jdbc_password}"></property>
	</bean>
	
	<!-- 動態數據源 -->
	<bean id="dataSource" class="com.test.spring.master.DynamicDataSource">
		<!-- 設置多個數據源 -->
		<property name="targetDataSources">
			<map key-type="java.lang.String">
				<!-- 這裏的key要和程序中保持一致 -->
				<entry key="master" value-ref="masterDataSource"></entry>
				<entry key="slave" value-ref="slaveDataSource"></entry>
			</map>
		</property>
		<!-- 默認數據源 -->
		<property name="defaultTargetDataSource" ref="masterDataSource"></property>
	</bean>
	
	
	<!-- 配置sqlSessionFactory -->
	<bean id="sqlSessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean">
		<!-- 數據源 -->
		<property name="dataSource" ref="dataSource" />
		<!-- 自動掃描mapping.xml文件 -->
		<property name="mapperLocations" value="classpath:com/test/spring/mapper/*.xml" />
	</bean>
	<!-- 配置mapper掃描器 -->
	<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
		<!-- 掃描包路徑,若是須要掃描多個包,用英文 逗號隔開 -->
		<property name="basePackage" value="com.test.spring.mapper" />
		<!-- 關聯mapper掃描器 與 sqlsession管理器 -->
		<property name="sqlSessionFactoryBeanName" value="sqlSessionFactoryBean" />
	</bean>

		
	<!-- 配置事務管理器 -->
	<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSource"></property>
	</bean>
	
	<!-- 配置事務屬性 -->
	<tx:advice id="txAdvice" transaction-manager="transactionManager">
		<tx:attributes>
			<!-- 定義查詢方法只讀 -->
			<tx:method name="query*" read-only="true"/>
			<tx:method name="find*" read-only="true"/>
			<tx:method name="get*" read-only="true"/>
			
			<!-- 主數據庫操做 -->
			<tx:method name="save*" propagation="REQUIRED"/>
			<tx:method name="update*" propagation="REQUIRED"/>
			<tx:method name="delete*" propagation="REQUIRED"/>
			
			<!-- 其它方法使用默認事務策略 -->
			<tx:method name="*"/>
			
		</tx:attributes>
	</tx:advice>
	
	<!-- 配置切面 -->
	<aop:config>
		<aop:pointcut expression="execution(* com.test.spring.service.impl.*Impl.*(..))" id="txPointcut"/>
		<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>
	</aop:config>
	
	<!-- 切面bean配置 -->
 	<bean id="dataSourceAspect" class="com.test.spring.master.DataSourceAspect"></bean>
	<aop:config>
		<aop:pointcut expression="execution(* com.test.spring.service.*.*(..))" id="aspectPointcut"/>
 		<aop:aspect ref="dataSourceAspect" order="-9999">
			<aop:before method="before2" pointcut-ref="aspectPointcut"/>
		</aop:aspect>
	</aop:config>
	
</beans>

###8.實現層緩存

package com.test.spring.service.impl;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.test.spring.bean.User;
import com.test.spring.mapper.UserMapper;
import com.test.spring.service.UserService;

@Service
public class UserServiceImpl implements UserService {
	
	@Autowired
	private UserMapper userMapper;
	
	/**
	 * 獲取用戶列表
	 */
	@Override
	public void getUserList() {
		
		System.out.println("走 從數據庫");
		
		List<User> listUser = userMapper.getUserList();
		System.out.println(listUser);
		
		System.out.println("================");
	}

	/**
	 * 更新用戶信息
	 */
	@Override
	public void updateUser(String userId,String userName) throws Exception{
		System.out.println("走 主數據庫");
		int sucess = userMapper.updateUser(userId, userName);
		System.out.println(sucess);
		throw new RuntimeException();
	}
}

###9.測試
親測,
1.能夠實現動態切換數據庫
2.事務也能夠起效
3.主從同步在mysql那一篇已經實現session

相關文章
相關標籤/搜索