SSM框架配置读写分离

  • Post author:
  • Post category:其他


一、前言

本篇博客是上一篇SSM框架搭建的后续,因为有人反应说,需要用到两个库,让我想起了项目中经常用到的读写分离的配置,所以查询了一下相关实现,中和几篇博客,挑选了一个比较简便的方式,测试,成功之后写在这里,希望给大家有帮助。(看这篇博文之前,请阅读SSM框架搭建:

https://blog.csdn.net/dwhdome/article/details/79131059

,在ssm框架搭建的基础上进行的延伸)

二、设置动态数据源

更改上一篇博文中applicationContext-dao.xml文件即可,原来是将唯一数据源交给sqlsessionfactory,现在是设置动态数据源,将其交给sqlsessionfactory。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p"
	xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="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/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
	http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.0.xsd">
 
	<!-- 数据库连接池 -->
	<!-- 加载配置文件 -->
	<context:property-placeholder location="classpath:*.properties" />
	<!-- 数据库连接池 (读)-->
	<bean id="readDataSource" class="com.alibaba.druid.pool.DruidDataSource"
		destroy-method="close">
		<property name="url" value="${read.jdbc.url}" />
		<property name="username" value="${read.jdbc.username}" />
		<property name="password" value="${read.jdbc.password}" />
		<property name="driverClassName" value="${read.jdbc.driver}" />
		<property name="maxActive" value="10" />
		<property name="minIdle" value="5" />
	</bean>
	
	<!-- 数据库连接池(写) -->
	<bean id="writeDataSource" class="com.alibaba.druid.pool.DruidDataSource"
		destroy-method="close">
		<property name="url" value="${write.jdbc.url}" />
		<property name="username" value="${write.jdbc.username}" />
		<property name="password" value="${write.jdbc.password}" />
		<property name="driverClassName" value="${write.jdbc.driver}" />
		<property name="maxActive" value="10" />
		<property name="minIdle" value="5" />
	</bean>
	
	<!-- 设置动态数据源 -->
	<!-- 这里的class路径是下一步要创建数据源切换的类的全路径,可根据自己创建的位置自行更改 -->
	<bean id="dynamicDataSource" class="com.util.DynamicDataSource">
		<property name="targetDataSources">
			<map key-type="java.lang.String">
				<!-- 设置读库数据源 -->
				<entry value-ref="readDataSource" key="readDataSource" />
				<!-- 设置写库数据源 -->
				<entry value-ref="writeDataSource" key="writeDataSource" />
				<!-- 如果有其他数据源,按照写库数据的配置再来一个就OK -->
			</map>
		</property>
		<!-- 配置默认使用的数据源为读库 -->
		<property name="defaultTargetDataSource" ref="readDataSource" />
	</bean>
	
	<!-- 让spring管理sqlsessionfactory 使用mybatis和spring整合包中的 -->
	<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
		<!-- 将动态数据源交给sqlsessionfactory -->
		<property name="dataSource" ref="dynamicDataSource" />
		<!-- 加载mybatis的全局配置文件 -->
		<property name="configLocation" value="classpath:mybatis/SqlMapConfig.xml" />
	</bean>


	<!-- 自动扫描 将Mapper接口生成代理注入到Spring -->
	<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
		<property name="basePackage" value="com.mapper" />
	</bean>
</beans>

把事务配置中的数据源相应的更改(


只更改了第15行的数据源


):applicationContext-trans.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p"
	xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="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/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
	http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.0.xsd">
	<!-- 事务管理器 -->
	<bean id="transactionManager"
		class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<!-- 数据源 -->
		<!-- 这里的数据源切换为applicationContext-dao.xml中配置的动态数据源,其他的不用改 -->
		<property name="dataSource" ref="dynamicDataSource" />
	</bean>
	<!-- 通知 -->
	<tx:advice id="txAdvice" transaction-manager="transactionManager">
		<tx:attributes>
			<!-- 传播行为 -->
			<tx:method name="save*" propagation="REQUIRED" />
			<tx:method name="insert*" propagation="REQUIRED" />
			<tx:method name="add*" propagation="REQUIRED" />
			<tx:method name="create*" propagation="REQUIRED" />
			<tx:method name="delete*" propagation="REQUIRED" />
			<tx:method name="update*" propagation="REQUIRED" />
			<tx:method name="find*" propagation="SUPPORTS" read-only="true" />
			<tx:method name="select*" propagation="SUPPORTS" read-only="true" />
			<tx:method name="get*" propagation="SUPPORTS" read-only="true" />
		</tx:attributes>
	</tx:advice>
	<!-- 切面 -->
	<aop:config>
		<aop:advisor advice-ref="txAdvice"
			pointcut="execution(* com.service.*.*(..))" />
	</aop:config>
</beans>

三、数据源切换

创建两个类。

1:创建DataSourceContextHolder类

package com.util;

public class DataSourceContextHolder {
	//存储当前线程的副本变量值。作用:每个线程执行的都会是默认数据源,设置当前线程的数据源不会影响其他线程。
	private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>();
	private static final String READ = "readDataSource";	//读库数据源名字,跟applicationContext-dao.xml中设置的名字要一致,用于切换数据源
	private static final String WRITE = "writeDataSource";  //写库数据源
	
	public static void setRead() {
		contextHolder.set(READ);
	}
	
	public static void setWrite() {
		contextHolder.set(WRITE);
	}

	public static String getDbType() {
		//获取当前线程变量值
		return ((String) contextHolder.get());
	}

	public static void clearDbType() {
		contextHolder.remove();
	}
	
}

2:创建名字为DynamicDataSource的类继承AbstractRoutingDataSource类

package com.util;

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

public class DynamicDataSource extends AbstractRoutingDataSource {
	@Override
	protected Object determineCurrentLookupKey() {
		//返回设置的数据源的名字,空值则用默认数据源
		return DataSourceContextHolder.getDbType();
	}
	
}

3:使用:在调用增删改查时 通过类名设置写库DataSourceContextHolder.setWrite();    //设置查询写库

四、写测试方法

1:在Controller增加一个查询写库的方法,复制一份查询方法,设置为写库即可

package com.controller;
 
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import com.pojo.User;
import com.service.UserService;
import com.util.DataSourceContextHolder;
 
@Controller
@RequestMapping("/user")  
public class UserController {
	
	@Resource(name="userService")
	private UserService userService;
	/**
	 * 根据id查询(查读库)
	 */
	@RequestMapping(value="/queryById")
	public ModelAndView queryById(HttpServletRequest request){
		ModelAndView mv = new ModelAndView();
		String id = request.getParameter("id");
		try{
			//默认读库
			User var = userService.findById(id);
			mv.setViewName("index");
			mv.addObject("var", var);
		} catch(Exception e){
            e.printStackTrace();
		}
		return mv;
	}
	
	/**
	 * 根据id查询(查写库)
	 */
	@RequestMapping(value="/queryByIds")
	public ModelAndView queryByIds(HttpServletRequest request){
		ModelAndView mv = new ModelAndView();
		String id = request.getParameter("id");
		try{
			DataSourceContextHolder.setWrite();	//设置查询写库
			User var = userService.findById(id);
			mv.setViewName("index");
			mv.addObject("var", var);
		} catch(Exception e){
            e.printStackTrace();
		}
		return mv;
	}
}

2:页面调用写库

<%@ page language="java" contentType="text/html; charset=utf-8"
    pageEncoding="utf-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<%
	String path = request.getContextPath();
	String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>查询用户</title>
</head>
<body>
	<form action="user/queryById.do" method="post">
		输入要查询的id(读库):	<input type="text" name="id" value="123456"/>
		<button type="submit">提交</button>
	</form>
	<form action="user/queryByIds.do" method="post">
		输入要查询的id(写库):	<input type="text" name="id" value="123456"/>
		<button type="submit">提交</button>
	</form>
</body>
</html>

五、效果演示

六、读写分离作用

在高并发下,减轻数据库访问压力,配置读写库同步。写库数据在有一定延迟的情况下会自动更新至读库。具体的自行百度,简单说一句 。

七、源码下载

链接:https://pan.baidu.com/s/1mKN1ZBYa7juOXMe-v3NlFg

提取码:8p5l

注:有任何问题加群:757477675



版权声明:本文为dwhdome原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。