mybatis之一级缓存和二级缓存

news2024/11/15 4:40:33

缓存:

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

缓存的概念:

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

使用缓存的原因:

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

适合缓存的数据:

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

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 mapper
        PUBLIC "-//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{
    @Test
    public 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{
    @Test
    public 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.coloradmin.cn/o/171271.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【RabbitMQ三】——RabbitMQ工作队列模式(Work Queues)

RabbitMQ工作队列模式为什么要有工作队列模式如何使用工作队列模式轮询消息确认验证消息确认消息持久化公平调度验证公平调度**现在将消费者1中的Thread.sleep(1000)改为Thread.sleep(3000);不添加公平调度相关代码进行测试。**现在将消费者1中的Thread.sleep(1000)改为Thread.…

BC即将登录Coinbase Institutional,2023年以全新姿态出发

以支付为最初定位的加密资产&#xff0c;在支付领域的发展始终停滞不前&#xff0c;尤其是在2022年&#xff0c;加密行业经历了几次“至暗时刻”&#xff0c;导致加密市场资金不断出逃市场全面转熊&#xff0c;越来越多的人对加密资产市场的发展前景失去信心。 而在2021年年底开…

【GD32F427开发板试用】移植CoreMark验证0等待区Flash大小

本篇文章来自极术社区与兆易创新组织的GD32F427开发板评测活动&#xff0c;更多开发板试用活动请关注极术社区网站。作者&#xff1a;Doravmon 引言 非常荣幸能够参与到此次GD32F427开发板试用的活动中来&#xff0c;在拿到开发板之前就翻了翻手册&#xff0c;一直有个疑问困惑…

APM/STM32F072RB基于HAL库配置USB CDC虚拟串口功能

APM/STM32F072RB基于HAL库配置USB CDC虚拟串口功能&#x1f4e2;采用的自制开发板&#xff0c;开源PCB工程详情放在《极海APM32F072RB开发环境测试》✨本案例基于STM32CubeMX工具配置。&#x1f4fa;使用STM32CubeMX工具配置工程改为APMF072RB型号过程如下&#xff1a; ⛳注意…

性能测试实战 | 电商业务的性能测试(一): 必备基础知识

本文为霍格沃兹测试学院优秀学员课程学习系列笔记&#xff0c;想一起系统进阶的同学文末加群交流。 1.1 测试步骤总览 需求分析与测试设计&#xff08;性能需求目标业务模型拆解&#xff09; 测试数据准备和构造(基于模型的数据准备) 性能指标预期(性能需求目标) 发压工具配…

vue2 使用@vue/composition-api依赖包 编译、打包各种报错

vue2 使用vue/composition-api依赖包 编译、打包各种报错问题来源解决办法最近在维护以前&#xff08;大概一年前&#xff09;的项目时&#xff0c;遇到个这种问题&#xff1a; 项目本身是用 vue-cli 创建的 vue 2.x.xx 版本的项目&#xff0c;然后引入 vue/composition-api 依…

MIT6.830-2022-lab5实验思路详细讲解

文章目录前言一、实验背景二、实验正文Exercise 1 &#xff1a;SearchExercise 2 &#xff1a;Insert - Splitting PagesExercise 3 &#xff1a;Delete - Redistributing pagesExercise 4&#xff1a;Delete - Redistributing pages总结前言 Datebase中很重要的一部分就是ind…

【Java面试】SpringBoot篇

注&#xff1a;本文通篇将SpringBoot以sb代替。 文章目录Spring和SpringBoot的关系和区别&#xff1f;谈谈你对SpringBoot的理解&#xff0c;它有哪些特性&#xff1f;SpringBoot的核心注解说说你对SpringBoot自动配置的理解为什么SpringBoot的jar包可以直接运行&#xff1f;Sp…

uboot启动流程分析(基于i.m6ull)

一、uboot的makefile 1.1 makefile整体解析过程 为了生成u-boot.bin这个文件&#xff0c;首先要生成构成u-boot.bin的各个库文件、目标文件。为了各个库文件、目标文件就必须进入各个子目录执行其中的Makefile。由此&#xff0c;确定了整个编译的命令和顺序。 1.2 makefile整…

10.2 初始泛型算法

文章目录只读算法find()count()accumulate()equal()写容器元素算法fill()fill_n()back_inserter()copy()copy_backward()replace()replace_copy()next_permutation()prev_permutation()重排容器元素算法sort()unique()stable_sort()除了少数例外&#xff0c;标准库算法都对一个…

pandas数据聚合与分组运算

文章目录数据聚合与分组运算分组与聚合的原理通过groupby()方法将数据拆分成组按列名进行分组按Series对象进行分组按字典进行分组按函数进行分组数据聚合与分组运算 对数据集进行分组并对各组应用一个函数&#xff08;无论是聚合还是转换&#xff09;&#xff0c;通常是数据分…

哈佛结构和冯诺依曼结构?STM32属于哈佛结构还是冯诺依曼结构?

现代的CPU基本上归为冯诺伊曼结构&#xff08;也成普林斯顿结构&#xff09;和哈佛结构。 冯诺依曼体系 冯诺依曼体系结构图如下 冯诺依曼结构也称普林斯顿结构&#xff0c;是一种将程序指令存储器和数据存储器合并在一起的存储器结构。数据与指令都存储在同一存储区中&…

大数据技术架构(组件)5——Hive:流程剖析2

1.1.2、Stage division&#xff08;不够细致&#xff0c;需要例子&#xff09;Stage理解&#xff1a;结合对前面讲到的Hive对查询的一系列执行流程的理解&#xff0c;那么在一个查询任务中会有一个或者多个Stage.每个Stage之间可能存在依赖关系。没有依赖关系的Stage可以并行执…

IIS部署应用程序连接 LocalDB 数据库

使用.net core框架创建ASP.NET Core API应用程序&#xff0c;利用Entity Framework core实体进行MS LocalDB数据库进行连接操作&#xff08;增/删/改/查运行&#xff09;。 问题&#xff1a; 在Visual Studio 2022 开发工具可以正常运行 Web API 应用程序连接 LocalDB 数据库…

R语言基于poLCA包进行潜类别分析

潜在类别分析是一种分析多元分类数据的统计技术。当观测数据以一系列分类响应的形式出现时- -例如&#xff0c;在民意调查、个人层面的投票数据、人与人之间可靠性的研究或消费者行为和决策中- -通常感兴趣的是调查观测变量之间的混淆来源&#xff0c;识别和表征相似案例的集群…

初步了解高性能队列——Disruptor(Java)

高性能队列——Disruptor ① 概述 Disruptor是英国外汇交易公司LMAX开发的一个高性能队列&#xff0c;研发的初衷是解决内部的内存队列的延迟问题&#xff0c;而不是分布式队列。基于Disruptor开发的系统单线程能支撑每秒600万订单&#xff0c;2010年在QCon演讲后&#xff0c…

DevOps利器之二(Git,Gitlab)

一、背景Git&#xff0c;Gitlab在DevOps中主要解决持续集成源码管控部分&#xff0c;本文主要从基本概念&#xff0c;实施部署两部分介绍。二、git概述https://git-scm.com/book/zh/v2 --推荐官方电子书 Git - 它是一个源代码分布式版本控制系统&#xff0c;可让开发人员在本地…

行业分享:锂电池4大生产难题,视觉检测即可有效解决

导语&#xff1a;机器视觉检测已在锂电池生产的各个环节中&#xff0c;为产品产量与质量提供可靠保障。维视智造作为锂电池视觉检测系统提供商&#xff0c;为企业提供专业、系统、稳定的锂电行业解决方案&#xff0c;可保证0漏检&#xff0c;确保安全生产&#xff0c;全面提升生…

Java总结(运算符)

1.算数运算符short s12;s1s12; &#xff08;编译不能运行)short s12;s1 2 ; (编译能运行&#xff0c;不改变变量本身的数据类型)2.逻辑运算符区分&和&&相同点&#xff1a;运算结果相同&#xff1b;当符号左边是true时&#xff0c;两者都会执行符号右边的运算不同点…

医疗数据安全实力派 | 美创科技品牌案例入选《2022年医疗行业网络安全报告》

近日&#xff0c;网络安全产业机构“数说安全”正式发布《2022年医疗行业网络安全报告》&#xff08;以下简称“报告”&#xff09;。报告对我国医疗行业信息化现状和政策、医疗行业市场发展、医疗行业需求侧及供给侧进行深度剖析。美创科技作为专业数据安全代表厂商入选医疗网…