一、前言
本篇博客是上一篇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