Spring Boot2(二):使用Spring Boot2集成Mybatis缓存机制
本文在个人技术博客【鸟不拉屎】同步发布,详情可猛戳 亦可扫描文章末尾二维码关注个人公众号【鸟不拉屎】
前言
学习SpringBoot集成Mybatis的第二章,了解到Mybatis自带的缓存机制,在部署的时候踩过了一些坑。在此记录和分享一下Mybatis的缓存作用。
本文章的源码再文章末尾
什么是查询缓存
MyBatis有一级缓存和二级缓存。记录可以看下这篇博文:
一级缓存
首先看一下什么是一级缓存,一级缓存是指SqlSession。一级缓存的作用域是一个SqlSession。Mybatis默认开启一级缓存。
在同一个SqlSession中,执行相同的查询SQL,第一次会去查询数据库,并写到缓存中;第二次直接从缓存中获取。当执行SQL查询前后发生增删改操作时,则SqlSession的缓存清空。
具体可以看这段代码:
@Test
public void testLocalCacheScope() throws Exception {
SqlSession sqlSession1 = factory.openSession(true);
SqlSession sqlSession2 = factory.openSession(true);
StudentMapper studentMapper = sqlSession1.getMapper(StudentMapper.class);
StudentMapper studentMapper2 = sqlSession2.getMapper(StudentMapper.class);
System.out.println("studentMapper读取数据: " + studentMapper.getStudentById(1));
System.out.println("studentMapper读取数据: " + studentMapper.getStudentById(1));
System.out.println("studentMapper2更新了" + studentMapper2.updateStudentName("小岑",1) + "个学生的数据");
System.out.println("studentMapper读取数据: " + studentMapper.getStudentById(1));
System.out.println("studentMapper2读取数据: " + studentMapper2.getStudentById(1));
}
开启两个sqlSession

从打印日志可以看出,前面两个说明sqlSession1的会话缓存生效了,第三个对sqlSession2会话执行了更新操作,这时候数据库发生数据变化,sqlSession2被清空。可是在执行第四个查询是,是查询的sqlSession1会话,由于sqlSession1没有被清空,所以还是查询的缓存的数据,是数据更新之前的,查询的是脏数据,一级缓存sqlSession是不共享的。证明了一级缓存只是在数据库会话内部共享的。
二级缓存
Mybatis的二级缓存是指mapper映射文件。二级缓存的作用域是同一个namespace下的mapper映射文件内容,多个SqlSession共享,Mybatis需要手动设置二级缓存。
在同一个namespace下的mapper文件中,执行相同的查询SQL,第一次会查询数据库,并写道缓存中;第二次z直接从缓存中获取。当执行SQL查询前后发生增删改操作时,则二级缓存清空。
上面说到二级缓存可以共享多个SqlSession。可以解决不同SqlSession回话中查询到脏数据的问题了。
SpringBoot整合Mybatis开启二级缓存
首先,Mybatis默认是开启一级缓存的,即同一个SqlSession每次查询都会去缓存中查询,没有数据的话,再去数据库获取数据。但是,整合到SpringBoot中后,一级缓存就会被关闭。为什么会出现这种原因呢,可以看下这篇文章:
好了,现在来创建项目,可以根据前一篇文章来创建项目,在这基础上修改
pom.xml新增mybatis缓存包caches
<dependency>
<groupId>org.mybatis.caches</groupId>
<artifactId>mybatis-ehcache</artifactId>
<version>1.1.0</version>
</dependency>
SysUserDao.xml添加开启Mybatis二级缓存
<cache />
加上这个标签,二级缓存就会开启,他的默认属性如下
映射语句文件中的所有 select 语句将会被缓存。
映射语句文件中的所有 insert,update 和 delete 语句会刷新缓存。
缓存会使用 Least Recently Used(LRU,最近最少使用的)算法来收回。
根据时间表(比如 no Flush Interval,没有刷新间隔), 缓存不会以任何时间顺序来刷新。
缓存会存储列表集合或对象(无论查询方法返回什么)的 1024 个引用。
缓存会被视为是 read/write(可读/可写)的缓存,意味着对象检索不是共享的,而且可以安全地被调用者修改,而不干扰其他调用者或线程所做的潜在修改。
也可以自定义二级缓存的属性,例如:
<cache
eviction="FIFO"
flushInterval="60000"
size="512"
readOnly="true"/>
这个更高级的配置创建了一个 FIFO 缓存,并每隔 60 秒刷新,存数结果对象或列表的 512 个引用,而且返回的对象被认为是只读的,因此在不同线程中的调用者之间修改它们会 导致冲突。
可用的收回策略有:
- LRU – 最近最少使用的:移除最长时间不被使用的对象。
- FIFO – 先进先出:按对象进入缓存的顺序来移除它们。
- SOFT – 软引用:移除基于垃圾回收器状态和软引用规则的对象。
- WEAK – 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。
默认的是 LRU。
flushInterval(刷新间隔)可以被设置为任意的正整数,而且它们代表一个合理的毫秒 形式的时间段。默认情况是不设置,也就是没有刷新间隔,缓存仅仅调用语句时刷新。
size(引用数目)可以被设置为任意正整数,要记住你缓存的对象数目和你运行环境的 可用内存资源数目。默认值是 1024。
readOnly(只读)属性可以被设置为 true 或 false。只读的缓存会给所有调用者返回缓 存对象的相同实例。因此这些对象不能被修改。这提供了很重要的性能优势。可读写的缓存 会返回缓存对象的拷贝(通过序列化) 。这会慢一些,但是安全,因此默认是 false。
测试验证
编写Controller接口
/**
* 查询所有用户信息
* @return
*/
@RequestMapping("/getAll")
private List<SysUserEntity> getUser() {
List<SysUserEntity> userList = sysUserService.queryUserAll();
return userList;
}
/**
* 根据userId查询用户信息
* @return
*/
@RequestMapping("/getUser")
private List<SysUserEntity> getUser(@RequestParam(value = "userId", required = false) Long userId) {
List<SysUserEntity> userList = sysUserService.queryUserInfo(userId);
return userList;
}
/**
* 更新用户信息
* @param user
* @return
*/
@RequestMapping("/updateUser")
private int updateUser(@RequestBody SysUserEntity user) {
return sysUserService.updateUserInfo(user);
}
通过postman发送接口请求进行测试:
1、发送查询用户全部信息:http://localhost:8080/getAll
2、根据userId查询用户信息:http://localhost:8080/getUser?userId=1
3、更新用户信息http://localhost:8080/updateUser
更新用户信息接口发送报文:
{
"userId":5,
"email":"12321321",
"mobile":"11111111111213"
}
通过日志可以看到,第一次发送1接口请求,对数据库进行了查询

可以看到,第二次和第三次查询没有查询数据库的SQL打印,而是去数据库获取数据
此时发送3接口,进行更新操作,在发送1接口,查询改用户的数据

可以看到,当执行数据库更新操作后,再进行查询,此时缓存已经清空,需要从数据库中重新查询获取。
这就演示了SpringBoot整合Mybatis的缓存机制测试。
总结
1、缓存的对象必须实现序列化。因为二级缓存的数据不一定都是存储到内存中,它的存储介质多种多样,所以需要给缓存的对象执行序列化,才可以确保获取无误。
2、Mybatis的二级缓存相比于一级缓存来说,实现了SqlSession之间的缓存数据的共享,做到namespace级别,粒度更细
3、在分布式环境下,由于默认的MyBatis Cache实现都是基于本地的,分布式环境下必然会出现读取到脏数据,需要使用集中式缓存将MyBatis的Cache接口实现,有一定的开发成本,直接使用Redis、Memcached等分布式缓存可能成本更低,安全性也更高。
不过建议Mybatis的缓存特性再生产环境下进行关闭,单纯作为一个ORM框架使用可能更加合适。
下篇文章计划写SpringBoot整合Mybatis,使用Redis实现缓存基本配置。
关于作者:
个人博客:鸟不拉屎
github主页:niaobulashi
github博客:鸟不拉屎
掘金:鸟不拉屎
博客园:鸟不拉屎
知乎:鸟不拉屎
微博:胡浪同學
公众号:鸟不拉屎
Spring Boot2(二):使用Spring Boot2集成Mybatis缓存机制的更多相关文章
- mybatis缓存机制
目录 mybatis缓存机制 Executor和缓存 一级缓存 小结 二级缓存 小结 mybatis缓存机制 mybatis支持一.二级缓存来提高查询效率,能够正确的使用缓存的前提是熟悉mybatis ...
- 开源框架是如何使用设计模式的-MyBatis缓存机制之装饰者模式
写在前面 聊一聊MyBatis是如何使用装饰者模式的,顺便回顾下缓存的相关知识,可以看看右侧目录一览内容概述. 装饰者模式 这里就不了它的概念了,总结下就是套娃.利用组合的方式将装饰器组合进来,增强共 ...
- 聊聊MyBatis缓存机制【美团-推荐】
聊聊MyBatis缓存机制 2018年01月19日 作者: 凯伦 文章链接 18778字 38分钟阅读 前言 MyBatis是常见的Java数据库访问层框架.在日常工作中,开发人员多数情况下是使用My ...
- 《深入理解mybatis原理4》 MyBatis缓存机制的设计与实现
<深入理解mybatis原理> MyBatis缓存机制的设计与实现 本文主要讲解MyBatis非常棒的缓存机制的设计原理,给读者们介绍一下MyBatis的缓存机制的轮廓,然后会分别针对缓存 ...
- Mybatis缓存机制及mybatis的各个组成部分
Mybatis 一级缓存: 基于PerpetualCache 的 HashMap本地缓存,其存储作用域为 Session,当 Session flush 或 close 之后,该Session中的所有 ...
- 聊聊MyBatis缓存机制
https://tech.meituan.com/mybatis_cache.html 前言 MyBatis是常见的Java数据库访问层框架.在日常工作中,开发人员多数情况下是使用MyBatis的默认 ...
- 【转】MyBatis缓存机制
转载:https://blog.csdn.net/bjweimengshu/article/details/79988252. 本文转载自公众号 美团技术点评 前言 MyBatis是常见的Java数据 ...
- MyBatis 缓存机制(十三)
什么是缓存 缓存就是内存中的一个对象,用于对数据库查询结果的保存,用于减少与数据库的交互次数从而降低数据库的压力,进而提高响应速度. MyBatis 缓存机制原理 Mybatis 缓存机制原理是将第一 ...
- 学习Spring Boot:(七)集成Mybatis
前面都是用的是spring data JPA,现在学习下Mybatis,而且现在Mybatis也像JPA那样支持注解形式了,也非常方便,学习一下. 数据库 mysql 5.7 添加依赖 在pom文件中 ...
随机推荐
- 从Client应用场景介绍IdentityServer4(三)
原文:从Client应用场景介绍IdentityServer4(三) 在学习其他应用场景前,需要了解几个客户端的授权模式.首先了解下本节使用的几个名词 Resource Owner:资源拥有者,文中称 ...
- Java中,对多线程访问同一变量(并发访问)的认识
在Java中,如果启动多个线程对同一个对象或者变量时候,在没有安全保护前提下有可能会抛出并异常 java.util.ConcurrentModificationException 当方法检测到对象的并 ...
- 七easy网络陷阱上当
网络犯罪可能开始与你或你的家人,因为无论出现什么样的警告信息,或异常体征,你还是做你通常做在互联网上,网络犯罪已经发生不知道.趋势科技收集了你所该避免的七种最常见的网络犯罪陷阱.让你和家人避免成为它们 ...
- 微博地址url(id)与mid的相互转换
关键字:新浪 微博 url id mid 互相转换地址:http://www.cnblogs.com/txw1958/archive/2012/12/07/weibo-id-to-mid.html 通 ...
- Arcgis for Javascript实现图
首先,截个图给大家看结果: 初始化状态 放大后的状态 点击选中后的状态 如上图所看到的,一般的涉及到的地图的统计涉及到上述所展示的三个状态:1.初始化状态.2.缩放后的状态:3.点击选中显示详情状态. ...
- GameBuilder见缝插针游戏开发系列(AA)
今天推出了一款游戏叫<AA>.在最近IOS只是弹出一个游戏.非常心脏的孩子,但有很多乐趣.今天,我们谈论它tangide(GameBuilderV2.0)用控件UICanvas实现它. 在 ...
- 【Linux计划】XSI IPC
三种IPC这就是所谓的XSI IPC,每间: 消息队列 信号量 共享存储器 以下分别介绍三种IPC的使用方法. 1.消息队列 消息队列是消息的链接表,具有例如以下函数接口: msgget:创建一个新队 ...
- TCP网络通讯如何解决分包粘包问题(有模拟代码)
TCP作为常用的网络传输协议,数据流解析是网络应用开发人员永远绕不开的一个问题. TCP数据传输是以无边界的数据流传输形式,所谓无边界是指数据发送端发送的字节数,在数据接收端接受时并不一定等于发送的字 ...
- uwp 沉浸式状态栏
//隐藏状态栏if (ApiInformation.IsTypePresent(typeof(StatusBar).ToString())) { StatusBar statusBar = Statu ...
- spring boot mybatis XML文件读取properties配置信息
配置文件application.properties中相关配置信息可以在部署以后修改,引用配置信息可以在代码和mybatis的映射文件中 1.JAVA代码 可以通过变量去读取 application. ...