MyBatis缓存详解(一级缓存、二级缓存、缓存查询顺序)

固态硬盘缺陷:无法长时间使用,而磁盘只要不消磁,只要不受到磁影响,就可以长期使用,因此绝大多数企业还是使用磁盘来存储数据

像mysql这种关系型数据库中的数据存储在磁盘中,为方便查询,减少系统开销,提高查询效率,会在内存中开辟空间用于存储经常查询、但不怎么改变的数据,这块空间称为缓存
在这里插入图片描述
MyBatis缓存分类

  1. 一级缓存
  2. 二级缓存

前置知识,MyBatis是如何执行的

private InputStream in = null;
    private SqlSession session = null;
    private UserDao mapper = null;

    @Before  //前置通知, 在方法执行之前执行
    public void init() throws IOException {
        //加载主配置文件,目的是为了构建SqlSessionFactory对象
        in = Resources.getResourceAsStream("SqlMapConfig.xml");
        //创建SqlSessionFactory对象
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
        //通过SqlSessionFactory工厂对象创建SqlSesssion对象
        session = factory.openSession();
  
        //通过Session创建UserDao接口代理对象
        mapper = session.getMapper(UserDao.class);

    }

    @After  //@After: 后置通知, 在方法执行之后执行 。
    public void destory() throws IOException {
        //释放资源
        session.close();
        in.close();
    }
}

在这里插入图片描述

1.一级缓存

默认情况下开启的缓存 放在SqlSession对象中

什么是对象→堆里面的一块内存空间

上述的SqlSession对象也是堆里面的一块空间,SqlSession对象里有一块一级缓存空间(SqlSession对象所在空间∈内存,cache缓存所在空间∈SqlSession对象所在空间,因此,之前说缓存是内存中的一块空间)
在这里插入图片描述
两次查询同一个数据,sql语句只会执行一次,第二次会从缓存中直接获取数据

@Test
    public void findById(){
        User user1 = mapper.findById(1);
        User user2 = mapper.findById(1);
        System.out.println(user1.toString());
        System.out.println(user2.toString());
        System.out.println(user1 == user2);  //在引用数据类型当中 == 是用来比较什么的? 是否指向同一个内存地址
    }

在这里插入图片描述

一级缓存失效的四种情况

1.sqlSession不同

一个SqlSession可以生成多个session对象,继而创建多个mapper

private InputStream in = null;
    private SqlSession session = null;
    private SqlSession session2 = null;
    private UserDao mapper = null;
    private UserDao mapper2 = null;

    @Before  //前置通知, 在方法执行之前执行
    public void init() throws IOException {
        //加载主配置文件,目的是为了构建SqlSessionFactory对象
        in = Resources.getResourceAsStream("SqlMapConfig.xml");
        //创建SqlSessionFactory对象
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
        //通过SqlSessionFactory工厂对象创建SqlSesssion对象
        session = factory.openSession();
        session2 = factory.openSession();
        //通过Session创建UserDao接口代理对象
        mapper = session.getMapper(UserDao.class);
        mapper2 = session2.getMapper(UserDao.class);

    }

    @After  //@After: 后置通知, 在方法执行之后执行 。
    public void destory() throws IOException {
        //释放资源
        session.close();
        in.close();
    }

    /*
     * sqlSession不同
     * 两个mapper不是同一个session生成的,故对应的开辟的内存空间也不同
     * */
    @Test
    public void findById5(){
        User user1 = mapper.findById(1);
        User user2 = mapper2.findById(1);
        System.out.println(user1 == user2);
    }
}

在这里插入图片描述
两个mapper不是同一个session生成的,故对应的开辟的内存空间也不同

2.sqlSession相同,查询条件不同,多次查询不同的情况,不会导致缓存失效

其实也很好理解,第一次查询的数据存储在缓存中,但第二次查询查询的是与第一次不同的数据,那缓存中必然是没有第二次查询的数据的

 /*
    sqlSession相同,查询条件不同
    */
    @Test
    public void findById1(){
        User user1 = mapper.findById(1);
        User user2 = mapper.findById(2);
        System.out.println(user1.toString());
        System.out.println(user2.toString());
        System.out.println(user1 == user2);
    }

在这里插入图片描述
3.sqlSession相同,查询的数据也相同,但两次查询之间执行了增删改操作

缓存要和数据库保持一致,如果进行了增删改操作却不修改缓存,那么可能造成缓存和数据库不一致的情况,所以执行增删改操作后缓存便失效了

/**
     * sqlSession相同,查询的数据也相同,但两次查询之间执行了增删改操作
     * 缓存要和数据库保持一致,如果进行了增删改操作不修改缓存,那么可能造成缓存和数据库不一致的情况!
     */
    @Test
    public void findById2(){
        User user1 = mapper.findById(1);
        mapper.insert(user1); //两次查询之间执行了增删改操作!
        User user2 = mapper.findById(1);
        System.out.println(user1 == user2);
    }

在这里插入图片描述
4.sqlSession相同,手动清除一级缓存

/*
    * sqlSession相同,手动清除一级缓存
     * */
    @Test
    public void findById3(){
        User user1 = mapper.findById(1);
        session.clearCache(); //清除缓存
        User user2 = mapper.findById(1);
        System.out.println(user1 == user2);
    }

session.clearCache()
在这里插入图片描述
这个也很好理解,第一次查询后缓存被清除了,第二次查询相同的数据时,缓存中已经没有了相应的数据,必然是要重新执行sql语句进行查询,开辟了新的内存空间,故指向地址也不一致了

2.二级缓存

二级缓存是SqlSessionFactory级别,通过同一个SqlSessionFactroy创建SqlSession查询结果会被缓存;此后若再次执行相同的查询语句,结果会从一个缓存中获取。

(1)二级缓存开启的条件

①:在核心配置文件中,设置全局属性caheEnabled="true"

②:在mapper映射文件中配置<cache/>

③:查询数据所转换的实体类类型必须实现序列化接口 implements Serializable

④:二级缓存必须在SqlSession(一级缓存)关闭或提交之后有效

(2)开启二级缓存

①.在SqlMapConfig.xml配置文件中开启二级缓存

<!‐‐ 开启二级缓存 ‐‐>
<settings>
    <!--开启二级缓存-->
    <setting name="cacheEnabled" value="true"/>
</settings>

②. 在UserMapper.xml配置文件声明使用二级缓存

<!--使用二级缓存-->
    <cache/>

③:查询数据所转换的实体类类型必须实现序列化接口

public class User  implements Serializable{
    ......
}

④:二级缓存必须在SqlSession关闭或提交之后有效

@Test
    public void findById4(){
        User user1 = mapper.findById(1);
        //关闭一级缓存
        session.close();
        User user2 = mapper2.findById(1);
        session2.close();
        System.out.println(user1 == user2);   // 一级缓存缓存的是对象,二级缓存缓存的是数据
    }

在这里插入图片描述
打印发现2个对象的地址值不一样,但是确实只发送了一次SQL语句的查询,二级缓存中存储的是数据,不是对象

一级缓存缓存的是对象(俩对象地址相同则相等),而二级缓存缓存的是数据,数据无法直接对比是否相同,故为false

(3)二级缓存失效的情况

两次查询之间行了任意的增删改,会使得一级二级缓存同时失效

这点与一级缓存一致,故不再赘述

(4)Catch参数的具体细节

缓存空间也是有限的,当满了之后,再进来新的数据,就要将之前的数据进行移除

eviction(收回策略)

  • LRU(最近最少使用的):移除最长时间不被使用的对象,这是默认值。
  • FIFO(先进先出):按对象进入缓存的顺序来移除它们。
  • SOFT(软引用):移除基于垃圾回收器状态和软引用规则的对象。
  • WEAK(弱引用):更积极地移除基于垃圾收集器状态和弱引用规则的对象。

flushinterval(刷新间隔)

可以被设置为任意的正整数,而且它们代表一个合理的毫秒形式的时间段。

  • 默认情况不设置,即没有刷新间隔,缓存仅仅在调用语句时刷新。

size(引用数目)

  • 可以被设置为任意正整数,要记住缓存的对象数目和运行环境的可用内存资源数目。默认值是1024 。

readOnly(只读)

属性可以被设置为 true / false。

  • true:只读缓存:**会给所有调用者返回缓存对象的相同实例。**因此这些对象不能被修改, 这提供了很重要的性能优势。
  • false读写缓存: 通过序列化返回缓存对象的拷贝,这种方式会慢一些,但是安全,因此默认是 false。

配置结果

<cache eviction="FIFO" flushInterval="6000" size="512" readOnly="true"/>   

3.Mybatis缓存查询顺序

  • 先查询二级缓存,因为二级缓存中可能会有其他程序查询出来的数据,可以直接拿来使用
  • 如果二级缓存未命中,再查询一级缓存
  • 如果一级缓存也没有命中,则查询数据库
  • SqlSession关闭之后,一级缓存的数据会写入二级缓存
    在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/901564.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Linux文件类型和根目录结构

Linux文件类型和根目录结构 1.文件类型 字符文件类型说明~普通文件类似于Windows的记事本d目录文件类似于windows文件夹c字符设备文件串行端口设备&#xff0c;顺序读写&#xff0c;键盘b块设备文件可供存储的接口设备&#xff0c;随机读写&#xff0c;硬盘p管道文件用于进程…

工程项目管理软件怎么选?推荐7款实用工具

本文提及的有主流7款工程项目管理系统软件有: 1. Worktile&#xff1b;2. 广联达BIM5D&#xff1b;3. 泛普软件&#xff1b;4. 明源云工程&#xff1b;5. 飞书&#xff1b;6. Smartsheet&#xff1b;7. Procore。 很多工程项目管理人员常常头疼如何有效地管理多个项目&#xff…

保研考研机试攻略:python笔记(1)

&#x1f428;&#x1f428;&#x1f428;宝子们好呀 ~ 我来更新欠大家的python笔记了&#xff0c;从这一篇开始我们来学下python&#xff0c;当然&#xff0c;如果只是想应对机试并且应试语言以C和C为主&#xff0c;那么大家对python了解一点就好&#xff0c;重点可以看高分篇…

【机器学习】——numpy教程

文章目录 1.numpy简介2.初始化numpy3.ndarry的使用3.1numpy的属性3.2numpy的形状3.3ndarray的类型 4numpy生成数组的方法4.1生成0和1数组4.2从现有的数组生成4.3生成固定范围的数组4.4生成随机数组 5.数组的索引、切片6.数组的形状修改7.数组的类型修改8.数组的去重9.ndarray的…

接口测试(七)jmeter——参数化(RandomString函数)

一、RandomString函数 需求&#xff1a;模拟10个用户注册 1. 【工具】–>【函数助手对话框】 2. 选择RandomString函数 假设手机号码前3位设置为固定数值136&#xff0c;后8位可用RandomString函数随机产生数值 ① Random string length&#xff1a;8&#xff08;随机长度…

记录element-ui改造select显示为table,并支持多查询条件

最近遇到的一个需求 &#xff0c; 很有趣&#xff0c;是需要一个select组件&#xff0c;要求显示工号&#xff0c;员工姓名&#xff0c;以及区域 三个字段&#xff0c;并且要支持三个字段的查询。显然element原生的组件不适用&#xff0c;这时候我们需要改造一下&#xff0c;把…

基于大数据 Python Vue 美食推荐可视化系统(源码+LW+部署讲解+数据库)

&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01; 会持续一直更新下去 有问必答 一键收藏关注不迷路 源码获取&#xff1a;https://pan.baidu.com/s/1aRpOv3f2sdtVYOogQjb8jg?pwdjf1d 提取码: jf1d &#…

Java后端面试题:Java基础篇

目录 Java基础 1.请你说说Java中基本数据类型的bit长度&#xff1f; 2.switch支持哪些数据类型&#xff1f;支持long么&#xff1f; 3.讲一下常见编码方式&#xff1f; 4.char能不能存储中文&#xff1f; 5.为什么数组索引从0开始呢&#xff1f;假如从1开始不行吗&#xf…

w003基于Springboot的图书个性化推荐系统的设计与实现

&#x1f64a;作者简介&#xff1a;多年一线开发工作经验&#xff0c;原创团队&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的网站项目。 代码可以查看文章末尾⬇️联系方式获取&#xff0c;记得注明来意哦~&#x1f339;赠送计算机毕业设计600个选题excel文…

Git 课程任务

破冰和创建项目 https://github.com/WangXiuhao/loveailab

CORE 安全与身份认证《1》 UseRouting 、UseEndpoints

认证、授权、确权与鉴权 路由 web的请求到达后端服务时&#xff0c;controller(控制器)会处理传入的http请求并响应用户操作&#xff0c; 请求的url会被映射到控制器的操作方法上。 此映射过程由应用程序中定义的路由规则完成。 》》》net core 在中间件中的鉴权授权的位置一…

rtp协议:rtcp包格式和传输间隔

RTP Control Protocol -- RTCP-rtp控制协议 实时传输控制协议&#xff08;RTCP&#xff09;基于对会话中的所有参与者定期传输控制包&#xff0c;使用与数据包相同的分发机制。底层协议必须提供数据包和控制包的多路复用&#xff0c;例如使用UDP时使用不同的端口号。RTCP执行四…

Golang | Leetcode Golang题解之第501题二叉搜索树中的众数

题目&#xff1a; 题解&#xff1a; func findMode(root *TreeNode) (answer []int) {var base, count, maxCount intupdate : func(x int) {if x base {count} else {base, count x, 1}if count maxCount {answer append(answer, base)} else if count > maxCount {ma…

一站式学习 Shell 脚本语法与编程技巧,踏出自动化的第一步

文章目录 1. 初识 Shell 解释器1.1 Shell 类型1.2 Shell 的父子关系 2. 编写第一个 Shell 脚本3. Shell 脚本语法3.1 脚本格式3.2 注释3.2.1 单行注释3.2.2 多行注释 3.3 Shell 变量3.3.1 系统预定义变量&#xff08;环境变量&#xff09;printenv 查看所有环境变量set 查看所有…

RK3568平台(基础篇)预编译命令原理

一.宏定义(#define)是什么 #define 可以将一对文本进行替换,在编译器读到需要被替换的文本的时候,会将这些文本全部替换成我们给定的文本。 如下是一个宏的定义: #define A 100 二.预编译 预处理命令 #if、#endif、#undef、#ifdef、#else、#elif 在接触#if、#undef这…

图片尺寸怎样能快速修改?图片改尺寸的4款在线工具

图片怎么压缩调整大小呢&#xff1f;现在的图片随着质量或者尺寸都会比较大&#xff0c;在很多平台上传时会导致无法使用&#xff0c;需要按照要求调整图片大小后才能够正常使用&#xff0c;那么如何操作能够快速修改图片大小呢&#xff1f;下面来给大家分享图片改大小的4款在线…

使用virtualenv/Anaconda/Miniconda创建python虚拟环境

自带venv 免安装直接使用 虚拟环境是创建一种隔离的工作空间&#xff0c;在该工作空间中可以安装不同的库&#xff0c;而不影响其他的项目。其中&#xff0c;python自带的venv&#xff0c;就可以很好的创建虚拟环境了。 # 创建虚拟环境 python -m venv venv# windows系统激活…

Linux -- 共享内存(2)

目录 命令 ipcs -m &#xff1a; 命令 ipcrm -m shmid&#xff1a; 共享内存的通信&#xff1a; 为什么共享内存更高效&#xff1f; 代码&#xff1a; ShmClient.cc&#xff1a; ShmServer.cc&#xff1a; 结果&#xff1a; 如何让共享内存实现同步&#xff1f; 代码&a…

119.WEB渗透测试-信息收集-ARL(10)

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 内容参考于&#xff1a; 易锦网校会员专享课 上一个内容&#xff1a;118.WEB渗透测试-信息收集-ARL&#xff08;9&#xff09; 释放完成后&#xff0c;点击创…

[mysql]子查询的概述和分类及单行子查询

子查询引入 查询的基本结构已经给大家了,子查询里面也是有一些新的内容,子查询其实就是在查询中嵌套另一个查询,叫嵌套查询可能大家更容易理解一点..,类似与FOR循环和FOR循环的嵌套,这一章是我们查询的最难的部分,大家 难度是查询的顶峰,多表查询和子查询是非常重要,SQL优化里…