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

news/2024/11/30 1:34:29/

目录

一、什么是缓存

二、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查询@Testpublic 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查询@Testpublic 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一级缓存@Testpublic 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.ppmy.cn/news/39133.html

相关文章

linux系统编程(6)--守护进程

1.终端概念 在UNIX系统中&#xff0c;用户通过终端登录系统后得到一个Shell进程&#xff0c;这个终端成为Shell进程的控制终端&#xff08;Controlling Terminal&#xff09;&#xff0c;进程中&#xff0c;控制终端是保存在PCB中的信息&#xff0c;而fork会复制PCB中的信息&a…

面向对象编程(基础)3:对象的内存解析

目录 3.1 JVM内存结构划分 3.2 对象内存解析 举例&#xff1a; 内存解析图&#xff1a; 面试题&#xff1a;对象名中存储的是什么呢&#xff1f; 3.3 练习 3.1 JVM内存结构划分 HotSpot Java虚拟机的架构图如下。其中我们主要关心的是运行时数据区部分&#xff08;Runtime …

使用ffmpeg生成测试视频和图片

要使用FFmpeg生成测试视频和图片&#xff0c;需要安装FFmpeg&#xff0c;并使用命令行工具执行相应的命令。 一、生成测试视频 创建一个测试视频源文件 test.mp4&#xff1a; ffmpeg -f lavfi -i testsrcduration5:size1280x720:rate30 test.mp4 上述命令可以生成一个时长…

Lambda表达式的使用

练习2&#xff1a; 定义一个接口(Flyable),里面定义一个抽象方法&#xff1a;void fly(String s); 定义一个测试类&#xff08;FlyableDemo&#xff09;&#xff0c;在测试类中提供两个方法 一个方法是&#xff1a;useFlyable(Flyable f) 一个方法是主方法&#xff0c;在主方…

一图看懂 stat 模块:解析 stat() 结果, 资料整理+笔记(大全)

本文由 大侠(AhcaoZhu)原创&#xff0c;转载请声明。 链接: https://blog.csdn.net/Ahcao2008 [TOC](一图看懂 stat 模块&#xff1a;解析 stat() 结果, 资料整理笔记&#xff08;大全&#xff09;) 摘要 全文介绍系统内置 stat 模块、函数、类及类的方法和属性。 它通过代码抓…

多线程之Thread常见的成员方法

String getName() 获取线程的名字 细节&#xff1a; 如果我们没有给线程设置名字&#xff0c;线程的默认名字是Thread-X&#xff08;序号&#xff0c;从0开始&#xff09; void SetName(String name) 设置线程的…

数据库及开发语言排行榜官网

目录 数据排行榜官网&#xff1a; 开发语言排行榜官网&#xff1a; 了解过软件开发的同学一定听说过数据库及开发语言排行榜&#xff0c;也在老师哪里看到过相关的截图&#xff0c;但是官网自己却一直找不到&#xff0c;下面就让博主把收集到的两个较为权威的官网分享给大家&…

Java重写(Override)重载(Overload)

目录 重写(Override)概述 重写(Override)讲解 重载(Overload)概述 重载(Override)讲解 重写(Override)概述 Java面向对象编程中的重写(override)指的是子类可以重写其父类中的非private方法,使得子类在调用该方法时会使用自己的实现而不是父类的实现。 重写(Override)…