深入理解MyBatis中的一级缓存与二级缓存
作者:阿木侠 发布时间:2022-05-25 09:41:34
前言
先说缓存,合理使用缓存是优化中最常见的,将从数据库中查询出来的数据放入缓存中,下次使用时不必从数据库查询,而是直接从缓存中读取,避免频繁操作数据库,减轻数据库的压力,同时提高系统性能。
一级缓存
一级缓存是SqlSession级别的缓存。在操作数据库时需要构造sqlSession对象,在对象中有一个数据结构用于存储缓存数据。不同的sqlSession之间的缓存数据区域是互相不影响的。也就是他只能作用在同一个sqlSession中,不同的sqlSession中的缓存是互相不能读取的。
一级缓存的工作原理:
用户发起查询请求,查找某条数据,sqlSession先去缓存中查找,是否有该数据,如果有,读取;
如果没有,从数据库中查询,并将查询到的数据放入一级缓存区域,供下次查找使用。
但sqlSession执行commit,即增删改操作时会清空缓存。这么做的目的是避免脏读。
如果commit不清空缓存,会有以下场景:A查询了某商品库存为10件,并将10件库存的数据存入缓存中,之后被客户买走了10件,数据被delete了,但是下次查询这件商品时,并不从数据库中查询,而是从缓存中查询,就会出现错误。
既然有了一级缓存,那么为什么要提供二级缓存呢?
二级缓存是mapper级别的缓存,多个SqlSession去操作同一个Mapper的sql语句,多个SqlSession可以共用二级缓存,二级缓存是跨SqlSession的。二级缓存的作用范围更大。
还有一个原因,实际开发中,MyBatis通常和Spring进行整合开发。Spring将事务放到Service中管理,对于每一个service中的sqlsession是不同的,这是通过mybatis-spring中的org.mybatis.spring.mapper.MapperScannerConfigurer
创建sqlsession自动注入到service中的。
每次查询之后都要进行关闭sqlSession,关闭之后数据被清空。所以spring整合之后,如果没有事务,一级缓存是没有意义的。
二级缓存
二级缓存原理:
二级缓存是mapper级别的缓存,多个SqlSession去操作同一个Mapper的sql语句,多个SqlSession可以共用二级缓存,二级缓存是跨SqlSession的。
UserMapper有一个二级缓存区域(按namespace分),其它mapper也有自己的二级缓存区域(按namespace分)。每一个namespace的mapper都有一个二级缓存区域,两个mapper的namespace如果相同,这两个mapper执行sql查询到数据将存在相同的二级缓存区域中。
开启二级缓存:
1,打开总开关
在MyBatis的配置文件中加入:
<span style="font-size:18px;"><settings>
<!--开启二级缓存-->
<setting name="cacheEnabled" value="true"/>
</settings> </span>
2,在需要开启二级缓存的mapper.xml中加入caceh标签
<span style="font-size:18px;"><cache/></span>
3,让使用二级缓存的POJO类实现Serializable接口
<span style="font-size:18px;">public class User implements Serializable {}</span>
测试一下
<span style="font-size:18px;">@Test
public void testCache2() throws Exception {
SqlSession sqlSession1 = sqlSessionFactory.openSession();
SqlSession sqlSession2 = sqlSessionFactory.openSession();
UserMapper userMapper1 = sqlSession1.getMapper(UserMapper.class);
User user1 = userMapper1.findUserById(1);
System.out.println(user1);
sqlSession1.close();
UserMapper userMapper2 = sqlSession2.getMapper(UserMapper.class);
User user2 = userMapper2.findUserById(1);
System.out.println(user2);
sqlSession2.close();
}</span>
输出结果:
<span style="font-size:18px;">DEBUG [main] - Cache Hit Ratio [com.iot.mybatis.mapper.UserMapper]: 0.0
DEBUG [main] - Opening JDBC Connection
DEBUG [main] - Created connection 103887628.
DEBUG [main] - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@631330c]
DEBUG [main] - ==> Preparing: SELECT * FROM user WHERE id=?
DEBUG [main] - ==> Parameters: 1(Integer)
DEBUG [main] - <== Total: 1
User [id=1, username=张三, sex=1, birthday=null, address=null]
DEBUG [main] - Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@631330c]
DEBUG [main] - Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@631330c]
DEBUG [main] - Returned connection 103887628 to pool.
DEBUG [main] - Cache Hit Ratio [com.iot.mybatis.mapper.UserMapper]: 0.5
User [id=1, username=张三, sex=1, birthday=null, address=null]</span>
我们可以从打印的信息看出,两个sqlSession,去查询同一条数据,只发起一次select查询语句,第二次直接从Cache中读取。
前面我们说到,Spring和MyBatis整合时, 每次查询之后都要进行关闭sqlSession,关闭之后数据被清空。所以spring整合之后,如果没有事务,一级缓存是没有意义的。那么如果开启二级缓存,关闭sqlsession后,会把该sqlsession一级缓存中的数据添加到namespace的二级缓存中。这样,缓存在sqlsession关闭之后依然存在。
总结:
对于查询多commit少且用户对查询结果实时性要求不高,此时采用mybatis二级缓存技术降低数据库访问量,提高访问速度。
但不能滥用二级缓存,二级缓存也有很多弊端,从MyBatis默认二级缓存是关闭的就可以看出来。
二级缓存是建立在同一个namespace下的,如果对表的操作查询可能有多个namespace,那么得到的数据就是错误的。
举个简单的例子:
订单和订单详情,orderMapper、orderDetailMapper。在查询订单详情时我们需要把订单信息也查询出来,那么这个订单详情的信息被二级缓存在orderDetailMapper的namespace中,这个时候有人要修改订单的基本信息,那就是在orderMapper的namespace下修改,他是不会影响到orderDetailMapper的缓存的,那么你再次查找订单详情时,拿到的是缓存的数据,这个数据其实已经是过时的。
根据以上,想要使用二级缓存时需要想好两个问题:
1)对该表的操作与查询都在同一个namespace下,其他的namespace如果有操作,就会发生数据的脏读。
2)对关联表的查询,关联的所有表的操作都必须在同一个namespace。
好了,以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流,谢谢大家对脚本之家的支持。
来源:http://blog.csdn.net/weixin_36380516/article/details/73194758


猜你喜欢
- 在.NET 4.0(当然也包括4.0以前的版本)下,用反射判断某个方法是否运用了自定义Attribute时,可以通过调用MethodInfo
- 文章来源:aspcn 作者:孙雯服务器Sockets列表9.2是一个服务器应用程序的一部分.列表9.2 一个简单的服务器程序 /** &n
- 本文实例讲述了Android编程入门之HelloWorld项目目录结构。分享给大家供大家参考,具体如下:我们介绍了如何搭建Android开发
- 根据UGUI的射线检测机制获取当前鼠标下的UI:/// <summary> /// 获取鼠标停留处UI
- 本文实例讲述了Java实现的读取资源文件工具类ResourcesUtil。分享给大家供大家参考,具体如下:package com.gclou
- 1、特效按钮的进展 之前的思路:css设置div的样式,在js中实现div对事件的响应,并改变div的样式,以实现动画效果。 1:以动画的形
- MainApplication类文件的位置搭建 SpringBoot 项目时有一个主程序入口类.这个 MainApp 类必须在放在整个项目的
- 概述使用this()或target()可绑定被代理对象实例,在通过类实例名绑定对象时,还依然具有原来连接点匹配的功能,只不过类名是通过增强方
- 说明一下性能方面 还可以接受 循环1000次普通Switch是用了0.001秒 ,扩展函数为0.002秒 , 如果是大项目在有负
- 关于滑动冲突在Android开发中,如果是一些简单的布局,都很容易搞定,但是一旦涉及到复杂的页面,特别是为了兼容小屏手机而使用了Scroll
- 本文实例为大家分享了unity使用socket实现聊天室的具体代码,供大家参考,具体内容如下unity聊天室服务端实现using Syste
- FreeMarker是一个模板引擎,一个基于模板生成文本输出的通用工具,使用纯Java编写FreeMarker被设计用来生成HTML Web
- 委托定义类型,类型指定特定方法签名。可将满足此签名的方法(静态或实例)分配给该类型的变量,然后(使用适当参数)直接调用该方法,或将其作为参数
- 前言在mybatis和mybatis plus里,如果你的实体字段是一个枚举类型,而在数据表里是整型,这时在存储时需要进行处理,默认情况下,
- spring boot 请求后缀匹配spring boot 项目中添加这个类可以实现url不同后缀区分了public class UrlMa
- Java基础编写猜数游戏,供大家参考,具体内容如下设计内容及要求产生一个100以内的随机数,游戏者进行猜数,猜中即胜,猜不中,提示是大了还是
- 可以用DSA和RSA,如: using System; using System.Text; using Sy
- @Value注解内使用SPEL自定义函数@Value("#{T(com.cheetah.provider.utils.String
- 由于考虑到数据库的安全性,不被轻易SQL注入,执行查询语句时,一般不使用直接拼接的语句,而是使用参数传递的方法。然后在使用参数传递的方法中时
- 前言springmvc中有两种很普遍的AOP实现:1.过滤器(Filter)2. * (Interceptor)本篇面对的是一些刚接触spr