mybatis之一级缓存和二级缓存

news/2023/6/7 1:25:23

缓存:

查询需要连接数据库,非常的耗费资源,将一次查询的结果,暂存在一个可以直接取到的地方,我们将其称之为缓存,当我们需要再次查询相同的数据时,直接走缓存这个过程,就不用走数据库了

缓存的概念:

存在内存中的临时数据,通过将用户经常查询的数据放在缓存[内存]中,用户去查询数据就不用从磁盘上(关系数据库数据文件)查询,从缓存中查询,从而提高查询效率,解决了高并发系统的性能问题

使用缓存的原因:

减少和数据库的交互次数,减少系统开销,提高系统效率

适合缓存的数据:

经常查询并且不太变化的数据是比较适合缓存的

Mybatis缓存:

mybatis包含一个非常强大的查询缓存特性,他可以非常方便地定制和配置缓存,缓存可以极大的提升查询效率,mybatis系统中默认定义了两级缓存:一级缓存和二级缓存默认情况下,只有一级缓存开启{SqlSession级别的缓存,也称为本地缓存}

二级缓存需要手动开启和配置,它是基于namespace级别的缓存,为了提高扩展性,mybatis定义了缓存接口Cache,我们可以通过实现Cache接口来定义二级缓存

一级缓存:

一级缓存也叫本地缓存:SqlSession

与数据库同一次会话期间查询到的数据会放在本地缓存中,在以后的查询工作中,如果需要获取相同的数据,直接从缓存中拿,不用再去查询数据库

举例:

在这里插入图片描述

关于测试本案例的其他四个文件代码,在这篇文章有,这里就不过多赘述了,不过需要注意的是mybatis-config.xml文件中绑定的接口名不要忘记更换

一级缓存的使用:

在接口中编写方法:

package dao;
import org.apache.ibatis.annotations.Param;
import pojo.Student;
public interface StudentMapper {//根据id查询用户Student queryStudentById(@Param("id") int id);
}

StudentMapper.xml文件中编写SQL语句:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Config 3.0//EN""https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="dao.StudentMapper"><select id="queryStudentById" resultType="Student">select *  from student where id=#{id}</select>
</mapper>

实体类:

注:实体类所对应的数据表应提前创建好,并插入数据,注意字段一致

package pojo;
import lombok.Data;
@Data
public class Student {private int id;private String name;private  String password;
}

测试类:

使用同一个SqlSession查询同一组数组两次:

package dao.user;
import dao.StudentMapper;
import pojo.Student;
import utils.mybatis_utils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;public class MyTest{@Testpublic void getUserByLimit(){//打开会话SqlSession sqlSession= mybatis_utils.getSqlSession();StudentMapper studentMapper=sqlSession.getMapper(StudentMapper.class);//第一次查询id为2的数据Student student1=studentMapper.queryStudentById(2);System.out.println(student1);System.out.println("============================");//第二次查询id为2的数据Student student2=studentMapper.queryStudentById(2);System.out.println(student2);//判断student1和student2是否为同一个对象System.out.println(student1==student2);//关闭会话sqlSession.close();}
}

输出如下:

通过输出的日志文件,我们可以清楚的看到当查询同一个数据两次时,预编译过程只会加载一次,并且通过比较他两是否相等的输出结果,也可以看出,他两确实是连地址都是相同的,那么也足以说明,与数据库同一次会话期间查询到的数据会放在本地缓存中,在以后的查询工作中,如果需要获取相同的数据,直接从缓存中拿,不用再去查询数据库,也就是不需要进行预编译这个过程

在这里插入图片描述

一级缓存失效的情况:

1:查询不同数据

举例:

修改查询数据为id等于1和id等于2:

Student student1=studentMapper.queryStudentById(2);
System.out.println(student1);
System.out.println("============================");
Student student2=studentMapper.queryStudentById(1);
System.out.println(student2);
System.out.println("student1和student2是否相等?"+(student1==student2));

输出如下:

当两次查询的数据不同时,我们会发现,其预编译过程进行了两次,并且通过比较发现他两并不相等

在这里插入图片描述

2:增删改操作,可能会改变原来的数据,所以必定会刷新缓存

举例{这里我们以更新操作为例}:

此次我们进行的操作步骤共有3个:

//1:查询id为1的数据
Student student1=studentMapper.queryStudentById(1);
System.out.println(student1);//2:更新id为2的数据
studentMapper.updateStudent(new Student(2,"ABC","11000"));
System.out.println("============================");//3:再次查询id为1的数据
Student student2=studentMapper.queryStudentById(1);
System.out.println(student2);System.out.println("student1和student2是否相等?"+(student1==student2));

输出如下所示:

通过输出结果,我们会发现,在第一次查询完id为1的数据后,我们对id为2的数据进行了更新操作,虽然id为1的数据并没有做任何的改变,但它此时同样预编译了2次,即缓存失效

在这里插入图片描述

3:查询不同的.xml文件

不同的.xml文件绑定不同的接口,sqlSession.getMapper获取到的值都不相同了,缓存当然失效了

4:手动清理缓存

方法如下:

在第一次查询结束时,调用clearCache方法手动清除缓存

//查询id为1的数据
Student student1=studentMapper.queryStudentById(1);
System.out.println(student1);//手动清除缓存
sqlSession.clearCache();System.out.println("============================");//再次查询id为1的数据
Student student2=studentMapper.queryStudentById(1);
System.out.println(student2);System.out.println("student1和student2是否相等?"+(student1==student2));

输出如下:

即使我们两次查询的内容都为id等于1,但预编译过程也进行了两次,说明此时一级缓存失效

在这里插入图片描述

一级缓存默认是开启的,只在一次SqlSession中有效,也就是从开始连接到关闭连接的这个区间段

如下所示:

在这里插入图片描述

二级缓存:

二级缓存也叫全局缓存,由于一级缓存作用域太低了,所以诞生了二级缓存,它是基于namespace级别的缓存,一个名称空间,对应一个二级缓存

工作机制:

一个会话查询一条数据,这个数据就会被放在当前会话的一级缓存中如果当前会话关闭了,这个会话对应的一级缓存就没有了但是我们想要的是,会话关闭了,一级缓存中的数据被保存到二级缓存中,新的会话查询信息,就可以从二级缓存中获取内容,不同的mapper查出的数据会放在自己对应的缓存(map)中

默认情况下,只启用了本地的会话缓存[一级缓存],它仅仅对一个会话中的数据进行缓存,如果要启用全局的二级缓存,只需要在SQL 映射文件中添加:

方法1:

<cache/>

这个简单语句的效果如下:

1: 映射语句文件中的所有 select 语句的结果将会被缓存
2: 映射语句文件中的所有 insert、update 和 delete 语句会刷新缓存
3:	缓存会使用最近最少使用算法(LRU, Least Recently Used)算法来清除不需要的缓存4: 缓存不会定时进行刷新(也就是说,没有刷新间隔)。
5: 缓存会保存列表或对象(无论查询方法返回哪种)的 1024 个引用6:缓存会被视为读/写缓存,这意味着获取到的对象并不是共享的,可以安全地被调用者修改,而不干扰其他调用者或线程所做的潜在修改

注: 缓存只作用于 cache 标签所在的映射文件中的语句,如果你混合使用 Java API 和 XML 映射文件,在共用接口中的语句将不会被默认缓存,你需要在mapper接口上使用 @CacheNamespaceRef 注解指定缓存作用域

方法2:

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

上述两种方法的主要区别在于,使用方式1,如果实体类未实现序列化接口,那么会报错,而第二种方法因为设置了readOnly参数的值,因此不会出现这种情况

二级缓存的使用步骤:

1:开启全局缓存:

官方文档中虽然指出二级缓存是默认开启的,但我们最好在配置文件的settings标签中显式的写出来,原因是为了增强可读性,当别人阅读你的代码时,可以很快的知道二级缓存确实开启了

在这里插入图片描述

在核心配置文件的settings标签中增加:如下代码

<setting name="cacheEnabled" value="true"/>

2:在要使用二级缓存的Mapper.xml文件中开启

方法1:

<cache/>

如果你是用方法1,报错如下:

在这里插入图片描述

原因:<cache/>中的readOnly默认为false,而可读写的缓存会通过序列化返回缓存对象的拷贝,此时需要实体类(这里是User)实现Serializable接口或者配置readOnly=true

解决方法:对实体类进行序列化,如下所示

方法1:在实体类中实现序列化接口

implements Serializable

输出如下所示:

在这里插入图片描述

注:序列化是深拷贝,所以反序列化后的对象和原对象不是同一个对象,故哈希值不同,因此输出false

方法2:在cache标签中,将readOnly参数的值设置为true

<cache readOnly="true"/>

输出如下:

在这里插入图片描述

方法2:自定义参数

注:使用方法2未报上面那种错误的原因为:在设置参数时,我们将readOnly的值设置为true

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

3:测试类中测试

package dao.user;
import dao.StudentMapper;
import pojo.Student;
import utils.mybatis_utils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;public class MyTest{@Testpublic void getUserByLimit(){//同时开启两个SqlSession去查询同一个数据SqlSession sqlSession1= mybatis_utils.getSqlSession();SqlSession sqlSession2= mybatis_utils.getSqlSession();StudentMapper studentMapper1=sqlSession1.getMapper(StudentMapper.class);StudentMapper studentMapper2=sqlSession2.getMapper(StudentMapper.class);//使用会话1查询id为1的数据Student student1=studentMapper1.queryStudentById(1);System.out.println(student1);//使用会话2查询id为1的数据Student student2=studentMapper2.queryStudentById(1);System.out.println(student2);System.out.println(student1==student2);//同时关闭两个会话sqlSession1.close();sqlSession2.close();}
}

输出如下:

根据二级缓存的工作机制:

一个会话查询一条数据,这个数据就会被放在当前会话的一级缓存中如果当前会话关闭了,这个会话对应的一级缓存就没有了,但是我们想要的是,会话关闭了,一级缓存中的数据被保存到二级缓存中,新的会话查询信息,就可以从二级缓存中获取内容

而我们上述写法会话1和会话2同时打开同时关闭的做法显然不符合二级缓存工作机制。

在这里插入图片描述

因此,修改如下所示,我们先关闭SqlSession1后再查询数据,最后关闭SqlSession2:

在这里插入图片描述输出如下:

我们会发现当SqlSession1会话关闭后,当再次查询与会话1查询相同的数据时,并没有进行预编译的过程,原因是:一级缓存中的数据被保存到二级缓存中,新的会话查询信息就可以从二级缓存中获取内容,因此只进行一次预编译,且通过比较发现他两是同一个对象

在这里插入图片描述

Mybatis缓存原理:

只要开启了二级缓存,在同一个Mapper下就有效,所有的数据都会先放在一级缓存中,只有当会话提交,或者关闭的时候,才会提交到二级缓存中,图解如下所示

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系郑州代理记账网进行投诉反馈,一经查实,立即删除!

相关文章

机器学习之八大算法总结

总结这八大算法对比 八大算法都可以用调库实现,具体库如下: 细节对比 #消除警告 import warnings warnings.filterwarnings(ignore)特征缩放 # 归一化(0,1缩放)__(只适用于神经网络多分类) min_x,max_x np.min(x),np.max(x) x (x-min_x)/(max_x-min_x)# 标准化 x - np.me…

人工智能应用中有这八大不可轻忽的弱点

https://www.toutiao.com/i6636523591120716302/ 2018-12-19 10:20:59 人工智能正在推动一场新的工业革命&#xff0c;其影响范围小则涉及个人生活&#xff0c;大则影响国家安全&#xff0c;让人们在期待的同时&#xff0c;也带着些许忐忑。事实上&#xff0c;当前人工智能的发…

什么是八大智能?

主要有语言智能、数学逻辑智能、空间智能、身体运动智能、音乐智能、人际智能、自我认知智能、自然认知智能。八十年代&#xff0c;美国著名发展心理学家、哈佛大学教授霍华德加德纳博士提出多元智能理论&#xff0c;二十多年来该理论已经广泛应用于欧美国家和亚洲许多国家的幼…

老砒霜和小创创---小朋友,你这样就别怪叔叔坏心眼了

事情 创创是我儿子的小名&#xff0c;快两岁了&#xff0c;周末带他去了一个小花园玩&#xff0c;那里有个类似小隧道的玩具&#xff0c;小朋友可以从那里的一头钻进去&#xff0c;再从另一头钻出来&#xff0c;创创也想玩&#xff0c;但是有个四五岁大的小男孩堵在那个小隧道…

【Alpha】“北航社团帮”小程序v1.0测试报告

目录 测试计划、过程和结果后端单元测试后端压力测试测试结果指标解释前端测试授权登录与权限检查功能测试兼容性测试性能测试回答课程组问题测试中发现的bug场景测试测试矩阵出口条件测试计划、过程和结果 后端单元测试 助教可以在这里查看测试代码。我们对所有接口设计了单元…

《郭论》郭德纲/著 读后得2021-02-22

《郭论》郭德纲/著 读后得 一、历史 1、历史上的四次“清君侧”&#xff1a;刘濞“七国之乱”、安禄山“安史之乱”、元朝帖木儿、明朝朱棣。朱棣是唯一一次“清君侧”成功的一次。 2、解缙&#xff1a;明朝神童。19岁中进士&#xff0c;明成祖曾说&#xff1a;天下不可一日无…

能原谅吗?公式相声李宏烨幡然醒悟,从三个方面向郭德纲道歉

说起相声界最大的腕&#xff0c;一定要属德云社的郭德纲&#xff0c;由于德云社的发展壮大&#xff0c;郭老师的名气也水涨船高。刚进北京城的时候&#xff0c;郭老师见人就要点头哈腰&#xff0c;再看如今的郭德纲&#xff0c;曲协主席见了他也要让三分。 虽然郭德纲老师名气很…

Java设计模式-中介者模式Mediator

介绍 中介者模式&#xff08;Mediator Pattern&#xff09;&#xff0c;用一个中介对象来封装一系列的对象交互。中介者使各个对象不需要显式地相互引用&#xff0c;从而使其耦合松散&#xff0c;而且可以独立地改变它们之间的交互。中介者模式属于行为型模式&#xff0c;使代…