Mybatis一级缓存和二级缓存(带测试方法)

news2025/6/7 12:39:01

目录

一、什么是缓存

二、Mabtis一级缓存

(1)测试一级缓存

(2)清空一级缓存

三、Mybatis二级缓存

(1)开启二级缓存

(2)测试二级缓存


一、什么是缓存 

        缓存是内存当中一块存储数据的区域,目的是提高查询效率。MyBatis会将查询结果存储在缓存当中,当下次执行相同的SQL时不访问数据库,而是直接从缓存中获取结果,从而减少服务器的压力。

什么是缓存?

        存在于内存中的一块数据。

缓存有什么作用?

        减少程序和数据库的交互,提高查询效率,降低服务器和数据库的压力。

什么样的数据使用缓存?

        经常查询但不常改变的,改变后对结果影响不大的数据。

MyBatis缓存分为哪几类?

        一级缓存和二级缓存

如何判断两次Sql是相同的?

  1. 查询的Sql语句相同
  2. 传递的参数值相同
  3. 对结果集的要求相同
  4. 预编译的模板Id相同

二、Mabtis一级缓存

        MyBatis一级缓存也叫本地缓存。SqlSession对象中包含一个Executor对象,Executor对象中包含一个PerpetualCache对象,在该对象存放一级缓存数据。

        由于一级缓存是在SqlSession对象中,所以只有使用同一个SqlSession对象操作数据库时才能共享一级缓存。

        MyBatis的一级缓存是默认开启的,不需要任何的配置。

如下图所示:

(1)测试一级缓存

        其实测试方法很简单,就是通过使用相同和不同的SqlSession对象进行SQL查询,返回的对象的哈希值是否一样就可以知道了,如果返回的哈希值一样说明没有进行SQL查询,而是直接从缓存拿到对象来返回

        下面是使用相同的Session对象来执行查询,如果观察user1和user2的哈希值一样则说明确实开启了一级缓存,并没有进行查询,而是直接从缓存中拿数据。 

import com.mybatisstudy.mapper.UserMapper;
import com.mybatisstudy.pojo.User;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Test;

import java.io.InputStream;

public class TestUserMapper3 {
    // 测试使用同一个SqlSession查询
    @Test
    public void testCache1() throws Exception{
        InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml");
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        SqlSessionFactory factory = builder.build(is);
        SqlSession session = factory.openSession();

        // 使用同一个SqlSession查询
        UserMapper mapper1 = session.getMapper(UserMapper.class);
        UserMapper mapper2 = session.getMapper(UserMapper.class);

        User user1 = mapper1.findById(1);
        System.out.println(user1.hashCode());
        System.out.println("------------------------------------------");
        User user2 = mapper2.findById(1);
        System.out.println(user2.hashCode());

        session.close();
    }
}

执行结果 

        OK,确实返回的哈希值都是一样的,并且我们可以通过控制台输出显示它并没有进行查询而是直接从缓存中拿到对象并返回,所以这就是一级缓存 ,提高了查询效率。

        下面使用不同的SqlSession来进行测试一下,返回的哈希值是否一致 

// 测试使用不同SqlSession查询
    @Test
    public void testCache2() throws Exception{
        InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml");
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        SqlSessionFactory factory = builder.build(is);
        SqlSession session1 = factory.openSession();
        SqlSession session2 = factory.openSession();

        // 测试使用不同SqlSession查询
        UserMapper mapper1 = session1.getMapper(UserMapper.class);
        UserMapper mapper2 = session2.getMapper(UserMapper.class);

        User user1 = mapper1.findById(1);
        System.out.println(user1.hashCode());
        System.out.println("---------------------------------");
        User user2 = mapper2.findById(1);
        System.out.println(user2.hashCode());

        session1.close();
        session2.close();
    }

运行结果

        OK,可以看到,返回的哈希值不一样,并且从控制台输出显示也可以看到这里的确也进行了一次查询,因此可以证实,共享一级缓存确实是基于SqlSession对象的 

(2)清空一级缓存

        但是吧,如果缓存过多的话,也是会影响我们的查询效率的,所以这时候就需要清空缓存了,就像我们时不时要清理一下手机缓存否则就会很卡,是同样的道理,那怎么清空一级缓存呢?

进行以下操作可以清空MyBatis一级缓存:

  1. SqlSession 调用 close() :操作后SqlSession对象不可用,该对象的缓存数据也不可用。
  2. SqlSession 调用 clearCache() / commit() :操作会清空一级缓存数据。
  3. SqlSession 调用增删改方法:操作会清空一级缓存数据,因为增删改后数据库发生改变,缓存数据将不准确
// 清空Mybatis一级缓存
    @Test
    public void testCache3() throws Exception{
        InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml");
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        SqlSessionFactory factory = builder.build(is);
        SqlSession session = factory.openSession();

        UserMapper mapper1 = session.getMapper(UserMapper.class);
        UserMapper mapper2 = session.getMapper(UserMapper.class);

        User user1 = mapper1.findById(1);
        System.out.println(user1.hashCode());

        // 清空Mybatis一级缓存
        session.clearCache();

        System.out.println("-------------------------------");
        User user2 = mapper2.findById(1);
        System.out.println(user2.hashCode());

        session.close();
    }

执行效果

        OK,返回的哈希值也确实不一样, 但是我们有没有观察到这和上面使用不同的SqlSession对象来执行查询的时候,控制台输入显示有点不一样,那就是这里不用再建立JDBC连接,也有效了提高查询效率,所以我们偶尔还是要清空一下缓存才行

三、Mybatis二级缓存

  • MyBatis二级缓存也叫全局缓存。数据存放在SqlSessionFactory中,只要是同一个工厂对象创建的SqlSession,在进行查询时都能共享数据。一般在项目中只有一个SqlSessionFactory对象,所以二级缓存的数据是全项目共享的。
  • MyBatis一级缓存存放的是对象,二级缓存存放的是对象的数据。所以要求二级缓存存放的POJO必须是可序列化的,也就是要实现Serializable接口。
  • MyBatis二级缓存默认不开启,手动开启后数据先存放在一级缓存中,只有一级缓存数据清空后,数据才会存到二级缓存中。
  •                 SqlSession 调用 clearCache() 无法将数据存到二级缓存中。

(1)开启二级缓存

1. POJO类实现Serializable接口

import java.io.Serializable;

public class User implements Serializable {
    private int id;
    private String username;
    private String sex;
    private String address;
}

2. 在Mybatis配置文件添加如下设置

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

        这里有个额外知识,就是Mybatis配置文件的标签还得按照顺序来放的,否则就会以下编译错误;

The content of element type "configuration" must match "(properties?,settings?,typeAliases?,typeHandlers?,objectFactory?,

objectWrapperFactory?,reflectorFactory?,plugins?,environments?,

databaseIdProvider?,mappers?)". 

        同时也说明了放置顺序就得按照match里面的顺序来放

3. 添加 <cache /> 标签

        如果查询到的集合中对象过多,二级缓存只能缓存1024个对象引用。可以通过

<cache /> 标签的size属性修改该数量。

        比如:<cache size="2048"/>

(2)测试二级缓存

        那怎么测试呢,从上面我们可以知道二级缓存存放的是对象的数据,并且是基于SqlSessionFactory的,因此我们可以用SqlSessionFactory获取两个SqlSession对象,然后让他们分别获取各自的mapper,然后进行查询,返回到同一个实例化的USer对象中,如果返回的数据是一致的,但是对象的哈希值是不一样的话,则说明二级缓存里存放的确实对象的数据而不是对象。


// 测试二级缓存
@Test
public void testCache4() throws Exception {
    InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml");
    SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
    SqlSessionFactory factory = builder.build(is);
    SqlSession session = factory.openSession();

    UserMapper mapper1 = session.getMapper(UserMapper.class);
    UserMapper mapper2 = session.getMapper(UserMapper.class);

    User user1 = mapper1.findById(1);
    System.out.println(user1);
    System.out.println(user1.hashCode());

    // 让一级缓存失效
    session.commit();
    System.out.println("----------------------------");

    user1 = mapper2.findById(1);
    System.out.println(user1);
    System.out.println(user1.hashCode());
}

运行结果 

        OK,从运行结果上我们可以知道结果集返回到同一个对象中,而他们的哈希值反而不一样,说明执行第二次查询的时候新建了一个对象并且该对象指向那个对象并且将SqlSessionFactory中的数据赋值到新建的那个对象。其实从控制台打印的日志我们也可以得出,并没有执行查询方法,因为没有打印SQL语句,而且缓存也是从0.0改成了0.5,因此我们可以断定二级缓存存放的是数据而不是对象。

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

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

相关文章

蓝桥杯嵌入式第十一届省赛题目解析

写完第十一届蓝桥杯嵌入式省赛题目&#xff0c;拿出来给大家参考参考&#xff0c;也是让大家一起测试看看有什么问题还需要改进&#xff0c;代码在最后喔。 目录 客观题&#xff1a; 程序设计题 &#xff1a; 题目解析&#xff1a; CubeMX配置 代码演示 &#xff1a; 客观…

Windows环境下实现设计模式——职责链模式(JAVA版)

我是荔园微风&#xff0c;作为一名在IT界整整25年的老兵&#xff0c;今天总结一下Windows环境下如何编程实现职责链模式&#xff08;设计模式&#xff09;。 不知道大家有没有这样的感觉&#xff0c;看了一大堆编程和设计模式的书&#xff0c;却还是很难理解设计模式&#xff…

spring boot Websocket(使用笔记)

使用websocket有两种方式&#xff1a;1是使用sockjs&#xff0c;2是使用h5的标准。使用Html5标准自然更方便简单&#xff0c;所以记录的是配合h5的使用方法。 1、pom 核心是ServerEndpoint这个注解。这个注解是Javaee标准里的注解&#xff0c;tomcat7以上已经对其进行了实现&a…

学内核之十八:纸上得来终觉浅,绝知此事要躬行

目录 0 前言 1 ioremap、vmalloc与原子上下文 2 copy_to_user与进程上下文 3 fasync与指针初始化 4 wait_event_interruptible与条件变量 0 前言 大家都知道&#xff0c;内核开发跟应用开发&#xff0c;体验是完全不同的&#xff0c;尤其是驱动。一方面要掌握扎实的语言基…

MySQL 基本轮廓

目录 什么是数据库 主流数据库 基本使用 连接服务器 服务器管理 使用案例 创建数据库 使用数据库 创建数据库表 表中插入数据 查询表中的数据 服务器&#xff0c;数据库&#xff0c;表关系 MySQL架构 什么是数据库 存储数据用文件就可以了&#xff0c;为什么还要弄…

每日一问-ChapGPT-20230409-中医基础-四诊之望诊

文章目录每日一问-ChapGPT系列起因每日一问-ChapGPT-20230409-中医基础-四诊之望诊中医中的望闻问切介绍&#xff0c;以及对应的名家望诊的具体细节望诊拓展当日总结每日一问-ChapGPT系列起因 近来看了新闻&#xff0c;看了各种媒体&#xff0c;抖音&#xff0c;官媒&#xff…

【数据库原理 • 四】数据库设计和规范化理论

前言 数据库技术是计算机科学技术中发展最快&#xff0c;应用最广的技术之一&#xff0c;它是专门研究如何科学的组织和存储数据&#xff0c;如何高效地获取和处理数据的技术。它已成为各行各业存储数据、管理信息、共享资源和决策支持的最先进&#xff0c;最常用的技术。 当前…

jvm调优一:从源码级别了解jvm类加载机制

目录 一、类加载运行全过程 类加载器加载类的过程 二、类加载器和双亲委派机制 类加载器类型 类加载器初始化过程 双亲委派机制 为什么要设计双亲委派机制&#xff1f; 全盘负责委托机制 一、类加载运行全过程 当我们用java命令运行某个类的main函数启动程序时&#xff0c…

Kube-proxy 使用 iptables 模式时,通过 Service 服务发布入口如何到达 Pod ?

写在前面 被问到这个问题&#xff0c;整理相关的笔记当 kube-proxy 模式设置为 iptables 的时候&#xff0c;通过 SVC 服务发布入口如何到达 Pod&#xff1f;博文内容涉及&#xff1a; 问题简单介绍三种常用的服务发布方式到Pod报文路径解析 当前集群为版本为v1.25.1Demo 演示使…

linux内核结构以及内核模块编程

1、linux内核结构 1.1、单内核与微内核结构 1.1.1、什么是单内核结构和微内核结构 linux操作系统是一个单内核的结构&#xff0c;它的各个子系统之间可以直接调用 比如说文件系统、内存管理、进程管理以及网络系统和进程间通信它们互相之间可以直接调用只有一些核心的代码它…

记录npm的安装过程

一、访问官网&#xff08;https://nodejs.org/en&#xff09;&#xff0c;下载nodejs并安装&#xff1a; 然后一路点击next直到安装完成&#xff0c;环境变量已经自动添加好了&#xff1a; 通过设置环境变量&#xff0c;改变本地仓库地址&#xff1a; 可以看到&#xff0c;…

一条更新语句的执行流程又是怎样的呢?

当一个表上有更新的时候&#xff0c;跟这个表有关的查询缓存会失效&#xff0c;所以这条语句就会把表T上所有缓存结果都清空。这也就是我们一般不建议使用查询缓存的原因。 接下来&#xff0c;分析器会通过词法和语法解析知道这是一条更新语句。优化器决定要使用ID这个索引。然…

LNMP网站框架搭建(yum方式安装)

1. nginx 的yum安装 1.1 搭建nginx相关的yum源 注意&#xff1a;本次安装所获得的软件包都是来源于httpd源&#xff08;都是由该软件包厂商提供&#xff09;。所以切记不能像往常一样直接使用本地源去安装一切包 vim /etc/yum.repos.d/nginx.repo [nginx-stable] namenginx…

Linux--tty

Linux 终端(TTY) TTY 是 Teletype 或 Teletypewriter 的缩写&#xff0c;原来是指电传打字机&#xff0c;后来这种设备逐渐键盘和显示器取代。不管是电传打字机还是键盘显示器&#xff0c;都是作为计算机的终端设备存在的&#xff0c;所以 TTY 也泛指计算机的终端(terminal)设…

【CSS】更改用户界面样式 ① ( 更改鼠标样式 | 更改鼠标样式应用场景 | 代码示例 )

文章目录一、更改鼠标样式二、更改鼠标样式代码示例三、更改鼠标样式应用场景一、更改鼠标样式 为对象元素设置 cursor 样式 , 可以更改鼠标移动到该元素上的显示样式 ; cursor 样式常用属性值 : default : 默认鼠标样式 , 白色箭头鼠标 ;pointer : 小手形状 ;move : 移动 - …

C++——初始化列表 | explicit关键字 | static成员

文章目录&#x1f490;专栏导读&#x1f490;文章导读&#x1f337;初始化列表&#x1f33a;初始化列表的形式&#x1f33a;初始化列表的注意事项&#x1f337;explicit关键字&#x1f33a;单参数构造函数&#x1f33a;多参数构造函数&#x1f337;static成员&#x1f33a;stat…

SprigBoot学习笔记(五)

监控 监控的意义 可视化监控平台 监控原理 自定义监控指标 监控的意义 监控服务状态是否宕机 监控服务运行指标(内存、虚拟机、线程、请求等) 监控日志 管理服务(服务下线) 监控的实施方式 显示监控信息的服务器:用于获取服务信息,并显示对应的信息 运行的服务:启动时主动…

Node【五】内置模块 【http模块】

文章目录&#x1f31f;前言&#x1f31f;http模块&#x1f31f; 1.引入http模块&#x1f31f; 2.创建服务&#x1f31f; 3.添加头信息&#x1f31f; 4.搭建一个简单的服务器&#xff1a;&#x1f31f; 5.Request对象&#x1f31f; 6.Response对象&#x1f31f; 7.练习&#xff…

【力扣周赛】第340场周赛

【力扣周赛】第340场周赛6361&#xff1a;对角线上的质数题目描述解题思路6360&#xff1a;等值距离和题目描述解题思路6361&#xff1a;对角线上的质数 题目描述 描述&#xff1a;给你一个下标从 0 开始的二维整数数组 nums &#xff0c;返回位于 nums 至少一条 对角线 上的…

webgl-原生纹理贴图

踩坑&#xff1a; 1、图片不显示&#xff1a;图片分辨率为非2的幂次方&#xff0c;图片不能被渲染。图形变成黑方块 2的N次幂&#xff1a;1 2 4 8 16 32 64 128 256 512 1024 2048 4096…… 2、几何图形配置映射方式&#xff0c;顶点坐标和纹理坐标对应需要注意&#xff0c;构…