Mybatis中的缓存管理
Mybatis中的缓存管理
查询缓存工作原理:
查询语句 <---> 缓存 <----> DB
缓存内容:
Mybatis中存放的是查询的内容;
缓存对比:
类别 | 作用范围 | 生命周期 |
---|---|---|
一级缓存 | namespace | 和sqlsession的生存周期相同 |
二级缓存 | namespace | 和整个应用的生命周期相同 |
一级缓存:默认是开启的,无开关关闭;
二级缓存:默认是开启的,需要手动关闭,二级缓存可以外置第三方的产品。
配置缓存:
默认配置:
一级缓存默认是开启的,无需配置,也不能关闭;
二级缓存也是默认开启状态的,如果想要关闭的话在“全局配置文件”中配置;全局关闭之后后面的配置也就不起作用了;
<configuration>
<!--二级缓存的全局开关-->
<settings>
<setting name="cacheEnabled" value="false"/>
</settings>
</configuration>
局部关闭设置:
<!--在查询语句里添加设置:useCache="false"-->
<select id="selectStudentById2" resultType="Student" useCache="false">
select id,name,age,score from student where id=#{xx}
</select>
开启内置二级缓存:
在mapper文件中添加并配置标签:
<cache size="512" eviction="LRU" flushInterval="100000"/>
flushInterval:刷新间隔;单位是ms;
eviction:收回缓存的策略;LRU为默认值,移除最长时间不被使用的对象;
size:可以存储集合或者对象的引用数目;
readOnly:设置只读,默认值为false;
为什么配置在mapper文件中:由于Mybatis的二级缓存是和命名空间绑定的(作用范围),所以二级缓存需要配置在Mapper.xml 映射文件中或者在接口文件中(注解形式配置);
操作的实体类实现Serializable接口。
使用二级缓存:
证明二级缓存Demo
public void Test01() throws IOException {
SqlSession session = MyBatisUtil.getSqlSession();
studentDao = session.getMapper(IStudentDao.class);
//第一次查询
Student student = studentDao.selectStudentById(2);
System.out.println("第一次查询:"+student);
//关闭一级缓存
session.close();
SqlSession session2 = MyBatisUtil.getSqlSession();
studentDao = session2.getMapper(IStudentDao.class);
//第二次查询
Student student2 = studentDao.selectStudentById(2);
System.out.println("第二次查询:"+student2);
//关闭一级缓存
session2.close();
SqlSession session3 = MyBatisUtil.getSqlSession();
studentDao = session3.getMapper(IStudentDao.class);
//第三次查询
Student student3 = studentDao.selectStudentById(2);
System.out.println("第三次查询:"+student3);
//关闭一级缓存
session3.close();
}
输出日志文件:
[DEBUG] Cache Hit Ratio [com.abc.dao.IStudentDao]: 0.0
[DEBUG] ==> Preparing: select id,name,age,score from student where id=?
[DEBUG] ==> Parameters: 2(Integer)
[DEBUG] <== Total: 1
第一次查询:Student{id=2, name='李四', age=18, score=90.0}
[DEBUG] Cache Hit Ratio [com.abc.dao.IStudentDao]: 0.5
第二次查询:Student{id=2, name='李四', age=18, score=90.0}
[DEBUG] Cache Hit Ratio [com.abc.dao.IStudentDao]: 0.6666666666666666
第三次查询:Student{id=2, name='李四', age=18, score=90.0}
可以看到第一次查询访问DB,关闭了sqlsession之后一级缓存已经无效,但是第二次和第三次查询都成功命中了缓存;
过程:在一级缓存的sqlsession关闭之后,sqlsession就会把数据保存到二级缓存中,在这之后二级缓存就有了缓存数据;
刷新缓存Demo:
public void Test02() throws IOException {
SqlSession session = MyBatisUtil.getSqlSession();
studentDao = session.getMapper(IStudentDao.class);
//第一次查询
Student student = studentDao.selectStudentById(2);
System.out.println("第一次查询:"+student);
//关闭一级缓存
session.close();
SqlSession session2 = MyBatisUtil.getSqlSession();
studentDao = session2.getMapper(IStudentDao.class);
//执行增删改操作
studentDao.insertStudent(new Student("小吕",21,96));
//第二次查询
Student student2 = studentDao.selectStudentById(2);
System.out.println("第二次查询:"+student2);
//关闭一级缓存
session2.close();
}
日志文件:
[DEBUG] ==> Preparing: select id,name,age,score from student where id=?
[DEBUG] ==> Parameters: 2(Integer)
[DEBUG] <== Total: 1
第一次查询:Student{id=2, name='李四', age=18, score=90.0}
[DEBUG] ==> Preparing: insert into student (name,age,score) value (?,?,?)
[DEBUG] ==> Parameters: 小吕(String), 21(Integer), 96.0(Double)
[DEBUG] <== Updates: 1
[DEBUG] Cache Hit Ratio [com.abc.dao.IStudentDao]: 0.5
[DEBUG] ==> Preparing: select id,name,age,score from student where id=?
[DEBUG] ==> Parameters: 2(Integer)
[DEBUG] <== Total: 1
第二次查询:Student{id=2, name='李四', age=18, score=90.0}
第一次查询之后关闭sqlsession并进行了插入操作,然后再次查询时候依然访问DB,因为缓存被刷新了;
刷新缓存过程:
Mybatis缓存底部实现:
一级缓存和二级缓存的底层都是通过map来实现的
- 对应key : HashCode + statementId + SQL语句(对象的哈希值+mapper文件中执行SQL语句的标签的id+SQL语句)
- 对应value : 查询结果本身
缓存刷新
增删改操作会刷新一级缓存和二级缓存:刷新缓存实际上是将缓存中所有的Entry对象的value置为null。并未删除整个Entry对象,key仍保留;所以当发生刷新之后再次查询会显示本次命中缓存,但是却到DB中查询;
增删改默认影响二级缓存的,但也可以让其不影响:只需要在对应增删改的statement中添加flushCache="false"
<insert id="insertStudent" flushCache="false">
insert into student (name,age,score) value (#{name},#{age},#{score})
</insert>
所以综上,有两种情况是直接到DB中执行真正的查询:
- Map中根本不存在要查找的Entry,即没有要查找的key
- Map中存在要查找的key,但是它的value值为null
配置EHcache
EHcache是外置的二级缓存,Mybatis官方也提供了EHcache的缓存:https://github.com/mybatis/ehcache-cache>
配置:
- 首先导入两个jar:EHcache核心jar包,Mybatis和EHcache整合jar
<!-- https://mvnrepository.com/artifact/org.ehcache/ehcache -->
<dependency>
<groupId>org.ehcache</groupId>
<artifactId>ehcache</artifactId>
<version>3.8.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-ehcache -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-ehcache</artifactId>
<version>1.0.0</version>
</dependency>
在映射文件中添加:
<cache type="org.mybatis.caches.ehcache.EhcacheCache">
<property name="maxElementsInMemory" value="1000"/>
</cache>
其中中可以添加属性和它对应的值;
产生脏数据
脏数据产生:在关系型数据库中,经常使用多表联合查询,需要关联多个数据表才能得到查询结果,查询结果会被放在一个namespace的缓存中;涉及到其他表的增删改通常不在一个映射文件中,当其他的表的信息变更的时候,在当前的namespace缓存中查到的信息还是没有改变之前的数据。
避免脏数据的出现:这时需要用到参照缓存。当某几个表可以作为一个业务整体时,通常是让几个会关联的ER 表同时使用同一个二级缓存;当参照缓存更改之后就会重新到DB中查询;
<mapper namespace= "UserMapper" >
<cache-ref namespace= "RoleMapper" />
</mapper>
使用原则:
不能出现多个namespace操作一张表的情况。
原因:二级缓存的作用范围只是单个namespace。
使用单表查询为主,尽量避免关联数据被改变;
对于关联查询的数据表不要出现增删改等操作,容易出现脏数据。查询操作较多,增删改较少的应用;
Mybatis中的缓存管理的更多相关文章
- [原创]关于mybatis中一级缓存和二级缓存的简单介绍
关于mybatis中一级缓存和二级缓存的简单介绍 mybatis的一级缓存: MyBatis会在表示会话的SqlSession对象中建立一个简单的缓存,将每次查询到的结果结果缓存起来,当下次查询的时候 ...
- SSM-MyBatis-17:Mybatis中一级缓存(主要是一级缓存存在性的证明,增删改对一级缓存会造成什么影响)
------------吾亦无他,唯手熟尔,谦卑若愚,好学若饥------------- 缓存------------------------------------------> 很熟悉的一个 ...
- mybatis中的缓存问题
关于mybatis基础我们前面几篇博客已经介绍了很多了,今天我们来说一个简单的问题,那就是mybatis中的缓存问题.mybatis本身对缓存提供了支持,但是如果我们没有进行任何配置,那么默认情况下系 ...
- 关于mybatis中一级缓存和二级缓存的简单介绍
关于mybatis中一级缓存和二级缓存的简单介绍 mybatis的一级缓存: MyBatis会在表示会话的SqlSession对象中建立一个简单的缓存,将每次查询到的结果结果缓存起来,当下次查询的时候 ...
- SSM-MyBatis-18:Mybatis中二级缓存和第三方Ehcache配置
------------吾亦无他,唯手熟尔,谦卑若愚,好学若饥------------- 二级缓存 Mybatis中,默认二级缓存是开启的.可以关闭. 一级缓存开启的.可以被卸载吗?不可以的.一级缓存 ...
- MyBatis中的缓存1
1.应用程序和数据库交互的过程是一个相对比较耗时的过程 2.缓存存在的意义:让应用程序减少对数据库的访问,提升程序运行的xiaolv 3.MyBatis中默认SqlSession缓存开启 3.1 同 ...
- Mybatis中的缓存
Mybatis提供缓存查询功能,用于减轻数据库压力,提升数据查询能力. Mybatis中定义了两级缓存:包括一级缓存与二级缓存.示意图如下所示: 一.一级缓存 一级缓存的特点: 每一个SqlSessi ...
- MyBatis中二级缓存和延时加载同时开启的问题
首先,二级缓存默认不开启! 要配置 <setting name="cacheEnabled" value="true"/> 在MyBatis中:一级 ...
- 一次读懂mybatis中的缓存机制
缓存功能针对于查询(没听说果UPDATE,INSERT语句要缓存什么,都是直接执行的) 默认情况下,mybatis会启用一级缓存. 如果使用同一个session对象调用了相同的SELECT语句,则直接 ...
随机推荐
- python 类 专有方法
__init__ : 构造函数,在生成对象时调用 __del__ : 析构函数,释放对象时使用 __repr__ : 打印,转换 __setitem__ : 按照索引赋值 __getitem__: 按 ...
- Jenkins连接Git仓库时候报错Permission denied, please try again.
一.连接GIT仓库报错 Failed to connect to repository : Command : stdout: stderr: Permission denied, please tr ...
- JavaGC垃圾回收机制和常见垃圾回收算法
Java GC是在什么时候,对什么东西,做了什么事情?” 什么位置:大部分在堆中,还有方法区!!方法区的垃圾收集主要回收两部分内容:废弃常量和无用的类,当满了之后同样触发FullGC, HotSpot ...
- python中线程和进程的简单了解
python中线程和进程的简单了解 一.操作系统.应用程序 1.硬件:硬盘.cpu.主板.显卡........ 2.装系统(本身也是一个软件): 系统就是一个由程序员写出来的软件,该软件用于控制计 ...
- JS高阶---函数的prototype
思维导图 栈堆翻译为为stack (1)原型与原型链 概念一.原型对象 验证步骤: 1.打印Data函数的原型prototype 原型属性指向原型对象 ===ES源码结构分析示意=== 2.空对象 3 ...
- Grafana中mysql作为数据源的配置方法
需求 近期在使用python写一套模拟API请求的监控项目,考虑数据可视化这方面就采用grafana来呈现,下面来看看怎么弄. 数据源准备 首先安装好mysql,将监控的日志数据写入到mysql之中. ...
- 跟着ALEX 学python day4集合 装饰器 生成器 迭代器 json序列化
文档内容学习于 http://www.cnblogs.com/xiaozhiqi/ 装饰器 : 定义: 装饰器 本质是函数,功能是装饰其他函数,就是为其他函数添加附加功能. 原则: 1.不能修改被装 ...
- 《面向对象程序设计(java)》第十周学习总结
201871010115 马北<面向对象程序设计(java)>第十周学习总结 项目 内容 这个作业属于哪个课程 https://www.cnblogs.com/nwnu-daizh ...
- pikachu SQL-Injection
1.数字型注入(POST) 可以看到,这个参数提交是POST类型的,用burp. 测试参数id id='&submit=%E6%9F%A5%E8%AF%A2 可以看到有报错回显,而且根据回显可 ...
- 万字长文把 VSCode 打造成 C++ 开发利器
https://zhuanlan.zhihu.com/p/96819625 面对大量代码,在开发任务繁重场景下,VSCode 绝对是一把利器.本文虽以 C++为引,但在 python.php.java ...