MyBatis缓存机制要点解析以及如何与三方缓存组件Redis整合示例

news2024/11/20 19:31:54

文章目录

  • 一、MyBatis的一级缓存
    • 1、每个SqlSession都有自己的一级缓存
    • 2、同一个SqlSession但是查询条件不同
    • 3、 同一个SqlSession两次查询期间执行了任何一次增删改操作
  • 4、同一个SqlSession自己手动清空一级缓存
  • 二、MyBatis的二级缓存
    • 1、二级缓存的相关配置
  • 三、一级\二级缓存总结
  • 四、整合第三方缓存组件Redis

我们知道,在所有的数据管理的中,查询是最多的操作,如果有一些大量的重复的操作(比如用户总是查询同一条数据)。是否可以把上次查到的内容,放到缓存中,而不必去数据库中建立查询数据,造成资源上的浪费, MyBatis就为我们实现了缓存功能。 Mybatis 包含一个非常强大的查询缓存特性,它可以非常方便地配置和定制,缓存可以极大的提升查询效率。
在这里插入图片描述

MyBatis中默认定义了两级缓存,分别是一级缓存和二级缓存。一级缓存和二级缓存级别不一样,一级缓存默认开启。缓存只对查询功能有效

  • 默认情况下,只有一级缓存(SqlSession级别的缓存,也称为本地缓存(Local Cache))开启
  • 二级缓存需要手动开启和配置,二级缓存是基于Mapper(namespace)级别的缓存
  • 为了提高扩展性,MyBatis定义了缓存接口Cache。我们可以通过实现Cache接口来自定义二级缓存。
    在这里插入图片描述

一、MyBatis的一级缓存

一级缓存是SqlSession级别的,通过同一个SqlSession查询的数据会被缓存,下次查询相同的数据,就 会从缓存中直接获取,不会从数据库重新访问。但是,如何界定哪些数据会从缓存中取用呢?
在这里插入图片描述
对于缓存失效的四种情况:
使一级缓存失效的四种情况:

  1. 不同的SqlSession对应不同的一级缓存
  2. 同一个SqlSession但是查询条件不同
  3. 同一个SqlSession两次查询期间执行了任何一次增删改操作
  4. 同一个SqlSession两次查询期间手动清空了缓存
    y

1、每个SqlSession都有自己的一级缓存

先表明现象最后推出本质,这里我定义的CacheMapper接口只是简单的根据员工的id查询出员工。这里可以自己定义,如果对如何定义MyBatis不是很熟的可以看博客从0到1搭建MyBatis实例思路剖析,跟着做完就能入门MyBatis

    @Test
    public void testCache() throws IOException {
        SqlSession sqlSession = getSqlSession();
        CacheMapper mapper = sqlSession.getMapper(CacheMapper.class);
        Emp emp1 = mapper.getEmpById(1);
        System.out.println(emp1);
        System.out.println("========第二次调用========从缓存中取数据");
        Emp emp2 = mapper.getEmpById(1);
        System.out.println(emp2);
        System.out.println("========即使用的不是同一个Mapper,也同样从缓存中取(同一个sqlsession)========");
        CacheMapper emp3 = sqlSession.getMapper(CacheMapper.class);
        System.out.println(emp3);
        System.out.println("\n========一级缓存的范围在sqlsession中,换一个新的sqlsession就会再次用sql读取数据========");
        SqlSession sqlSession1 = getSqlSession();
        CacheMapper mapper1 = sqlSession1.getMapper(CacheMapper.class);
        Emp emp4 = mapper1.getEmpById(1);
        System.out.println(emp4);
    }

对结果进行分析
在这里插入图片描述

2、同一个SqlSession但是查询条件不同

给定两个不同的查询条件

 @Test
    public void testCache2() throws IOException {
        SqlSession sqlSession = getSqlSession();
        CacheMapper mapper = sqlSession.getMapper(CacheMapper.class);
        System.out.println("第一次获取数据,查询条件: eid = 1");
        Emp emp1 = mapper.getEmpById(1);
        System.out.println(emp1);
        System.out.println("第二次获取数据,查询条件:eid = 100");
        Emp emp2 = mapper.getEmpById(100);
        System.out.println(emp2);
    }

在这里插入图片描述
多提一嘴:对于所有的缓存结构,基本上可以猜到是键值对的形式。而通过这次分析,可以知道,对于键值对形式的缓存结构,key一定是和查询条件有关的。

3、 同一个SqlSession两次查询期间执行了任何一次增删改操作

对于缓存,我们知道最大的弊端之一可能就是缓存一致性的问题。如何保证用户从缓存查询到的数据一定是和数据库保持一致的,而不是保存老版的数据。其中一个操作就是,如果有增删改的操作就直接删缓存内容。
如果在MyBatis中进行了增删改,会将清空当前SqlSession会话下的LocalCache,这种比较极端,实际上最好的解决方案貌似是修改或者删除了哪条就删除缓存中的哪条。

   @Test
    public void testCache3() throws IOException {
        SqlSession sqlSession = getSqlSession();
        CacheMapper mapper = sqlSession.getMapper(CacheMapper.class);
        System.out.println("第一次获取数据,查询条件: eid = 1");
        Emp emp1 = mapper.getEmpById(1);
        System.out.println(emp1);
        Emp emp2 = mapper.getEmpById(1);
        System.out.println(emp2);
        System.out.println("\n=====进行增删改操作=====");
        mapper.updateNameByEid(100, "zhangsan");
        System.out.println("\n=====同一个sqlsession,再获取数据=====");
        Emp emp3 = mapper.getEmpById(1);
        System.out.println(emp3);
    }

最后的结果:
在这里插入图片描述

4、同一个SqlSession自己手动清空一级缓存

 @Test
    public void testCache4() throws IOException {
        SqlSession sqlSession = getSqlSession();
        CacheMapper mapper = sqlSession.getMapper(CacheMapper.class);
        System.out.println("第一次获取数据,查询条件: eid = 1");
        Emp emp1 = mapper.getEmpById(1);
        System.out.println(emp1);
        System.out.println("\n=====两次查询期间手动清空缓存=====");
        sqlSession.clearCache();
        System.out.println("第一次获取数据,查询条件: eid = 1");
        Emp emp2 = mapper.getEmpById(1);
        System.out.println(emp2);
    }

在这里插入图片描述

二、MyBatis的二级缓存

对于一级缓存,通过上述分析知道是属于SqlSessionFactory级别的。而二级缓存是SqlSessionFactory级别,通过同一个SqlSessionFactory创建的SqlSession查询的结果会被缓存;此后若再次执行相同的查询语句,结果就会从缓存中获取。
对于二级缓存MyBatis不是默认开启的,需要我们手动进行开启。
二级缓存开启的条件

  • 在核心配置文件中,设置全局配置属性cacheEnabled=“true",默认为true,不需要设置
  • 在映射文件中设置标签<cache />
  • 二级缓存必须在SqlSession关闭或提交之后有效
  • 查询的数据所转换的实体类类型必须实现序列化的接口

使二级缓存失效的情况: 两次查询之间执行了任意的增删改,会使一级和二级缓存同时失效。需要注意的是,如果没有提交sqlsession时,数据会保存在一级缓存中,提交后,会将一级缓存的内容刷到二级缓存中。
二级缓存执行图:
在这里插入图片描述
在映射文件中设置标签<cache />,比如说对于CacheMapper.xml开启。
在这里插入图片描述
最后直接开始测试,在测试之前需要注意的是:在你需要返回的实体类上实现接口implements Serializable
测试代码:

   @Test
    public void testCache5() throws IOException {
        // 二级缓存是基于sqlsessionfactory的
        // 加载核心配置文件
        InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
        // 获取sqlsessionFactory
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
        System.out.println("第一个sqlsession的操作");
        SqlSession sqlSession1 = sqlSessionFactory.openSession(false);
        System.out.println(sqlSession1.getMapper(CacheMapper.class).getEmpById(1));
        // 需要关闭/提交会话,才能将一级缓存刷到二级
        sqlSession1.commit(); // 提交,将一级缓存刷到二级缓存中
        System.out.println("=====二级缓存未打开,没从缓存中获取数据=====");
        SqlSession sqlSession2 = sqlSessionFactory.openSession(false);
        System.out.println(sqlSession2.getMapper(CacheMapper.class).getEmpById(1));
    }

在这里插入图片描述
开启二级缓存;
在这里插入图片描述

1、二级缓存的相关配置

其实对于xml中的cache标签还可以加很多属性

<!-- 声明这个namespace 使用二级缓存-->
<cache type="org.apache.ibatis.cache.impl.PerpetualCache"
	size="1024" <!—-最多缓存对象个数,默认1024-->
	eviction="LRU" <!—回收策略-->
	flushInterval="120000" <!—自动刷新时间ms,未配置时只有调用时刷新-->
	readOnly="false"/> <!—默认是false(安全),改为true 可读写时,对象必	须支持序列
化-->

cache属性详解:
在这里插入图片描述

三、一级\二级缓存总结

如果是开启了二级缓存,在MyBatis中总的查询流程如下所示:
在这里插入图片描述

  • 先查询二级缓存,因为二级缓存中可能会有其他程序已经查出来的数据,可以拿来直接使用。
  • 如果二级缓存没有命中,再查询一级缓存
  • 如果一级缓存也没有命中,则查询数据库
  • SqlSession关闭之后,一级缓存中的数据会写入二级缓存。

有关于二级缓存和一级缓存的架构位置,是先走的是CachingExecutor查看是否有二级缓存信息,之后在走一级缓存。
在这里插入图片描述

四、整合第三方缓存组件Redis

对于第三方组件的使用,前提条件就是开启了二级缓存。这个操作无非就是将原来的二级缓存的数据移植到了第三方组件中。至于为什么,其中一个就是mybatis自带的二级缓存,这个缓存是单服务器工作,无法实现分布式缓存。
mybatis提供了一个cache接口,如果要实现自己的缓存逻辑,实现cache接口开发即可。mybatis本身默认实现了一个,但是这个缓存的实现无法实现分布式缓存,所以我们要自己来实现。
首先,需要导入jar

 <!-- https://mvnrepository.com/artifact/org.mybatis.caches/mybatis-redis -->
    <dependency>
      <groupId>org.mybatis.caches</groupId>
      <artifactId>mybatis-redis</artifactId>
      <version>1.0.0-beta2</version>
    </dependency>

修改mapper配置文件中的type

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.fckey.mybatis.mapper.CacheMapper">
    <!--    开启二级缓存-->
    <!-- 声明这个namespace 使用二级缓存-->
    <!-- 开启二级缓存, 需要修改为 -->
    <cache type="org.mybatis.caches.redis.RedisCache"/>

resource文件夹下新建文件redis.properties (名字必须叫这个,不然无法获取到里面的数据)

redis.host=127.0.0.1 
redis.port=6379 
redis.pass=
redis.database=0 
redis.maxIdle=300 
redis.maxWait=3000 
redis.testOnBorrow=true 

测试代码:

   @Test
    public void testCache5() throws IOException {
        // 二级缓存是基于sqlsessionfactory的
        // 加载核心配置文件
        InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
        // 获取sqlsessionFactory
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
        System.out.println("第一个sqlsession的操作");
        SqlSession sqlSession1 = sqlSessionFactory.openSession(false);
        System.out.println(sqlSession1.getMapper(CacheMapper.class).getEmpById(1));
        // 需要关闭/提交会话,才能将一级缓存刷到二级
        sqlSession1.commit(); // 提交,将一级缓存刷到二级缓存中
        System.out.println("=====二级缓存打开,从缓存中获取数据=====");
        SqlSession sqlSession2 = sqlSessionFactory.openSession(false);
        System.out.println(sqlSession2.getMapper(CacheMapper.class).getEmpById(1));
    }
    

最后的结果;
在这里插入图片描述
最后我们来看看redis中的数据变化。
在这里插入图片描述


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

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

相关文章

外购设备PDA

专业扫描引擎&#xff0c;扫尽千军万码 工业级专业扫描引擎&#xff0c;数据采集精准、快速、安全&#xff1b; 同时增加摄像头扫描&#xff0c;自带绿点定位&#xff0c;实现快速对准&#xff1b; 可识别破损&#xff0c;沾染灰渍等条码提高工作效率。 一、产品特点 216GB/…

xhs xs _webmsxyw 纯算法还原盗用代码请注明出处搬来搬去真的很下头!

本文以教学为基准、本文提供的可操作性不得用于任何商业用途和违法违规场景。 本人对任何原因在使用本人中提供的代码和策略时可能对用户自己或他人造成的任何形式的损失和伤害不承担责任。 最新版 x-s 没露任何版权请审核员认真对待谢谢。 【2023.05.22】 更新全站接口通用 …

携手企企通,农业产业化国家重点龙头企业「罗牛山」加速采购数智化建设

导语 与企企通形成战略合作&#xff0c;双方基于供应商、合同管理、采购协同等多方面的应用场景&#xff0c;打造立足海南辐射全国的行业标准化解决方案。行业案例的示范作用&#xff0c;不仅对牛罗山采购业务数字化有指导意义&#xff0c;对整个畜牧养殖行业加入采购供应链管…

redis-实现限流

1、 主流的四种限流策略&#xff0c;我都可以通过redis实现 引言 在web开发中功能是基石&#xff0c;除了功能以外运维和防护就是重头戏了。因为在网站运行期间可能会因为突然的访问量导致业务异常、也有可能遭受别人恶意攻击 所以我们的接口需要对流量进行限制。俗称的QPS也是…

finallshell mac SSH工具

一、FinallShell 是什么 FinalShell是一体化的的服务器,网络管理软件,不仅是ssh客户端,还是功能强大的开发,运维工具,充分满足开发,运维需求. 特色功能: 云端同步,免费海外服务器远程桌面加速,ssh加速,本地化命令输入框,支持自动补全,命令历史,自定义命令参数 二、主要特性 …

CMake Practice 学习笔记四---使用动静态库

任务&#xff1a; 编写一个程序使用我们上一届构建的共享库 1、准备工作 在/backup/cmake目录建立t4目录 mkdir t4在t4目录中建立src目录&#xff0c;并编写源文件main.c cd t4 mkdir src && cd src touch main.cmain.c的内容如下&#xff1a; #include <hel…

博客系统后端设计(七) - 实现显示用户信息与注销功能

文章目录 1. 显示用户信息1.1 约定前后端交互接口1.2 修改列表页的前段代码1.3 实现详情页的后端代码1.4 实现详情页的前端代码 2. 注销2.1 确定前后端交互接口2.2 实现后端代码2.3 修改前端代码 1. 显示用户信息 此处的用户名是写死的&#xff0c;我们希望的是此处是能够动态生…

nodejs+vue婚庆服务网站的设计与实现

为了适应现代人类强烈的时间观念&#xff0c;对于用户&#xff0c;因此&#xff0c;这就需要一个互联网平台实现在线婚庆服务网站&#xff0c;正是这么一个方便的平台。本网站中&#xff0c;用户与活动报名可以以最方便的形式&#xff0c;在最短的时间内获悉报名信息&#xff0…

Nginx Web页面缓存 Rsync远程同步

Nginx Web页面缓存 在http块中加配置&#xff1a; proxy_cache_path /data/nginx/cache levels1:2 keys_zonemy_cache:10m max_size10g inactive60m use_temp_pathoff ##################################### path&#xff1a;强制参数&#xff0c;指定缓存文件的存放路径 …

51单片机蓝牙APP自助商品售卖机12864投币找零

实践制作DIY- GC0132-蓝牙APP自助商品售卖机 一、功能说明&#xff1a; 基于51单片机设计-蓝牙APP自助商品售卖机 二、功能介绍&#xff1a; 硬件组成&#xff1a;STC89C52单片机最小系统LCD12864显示蜂鸣器ULN2003步进电机模拟出商品多个按键&#xff08;找零、确认、投…

掌握RDD算子

文章目录 一、准备本地系统文件二、把文件上传到HDFS三、启动HDFS服务四、启动Spark服务五、启动Spark Shell六、映射算子案例任务1、将rdd1每个元素翻倍得到rdd2任务2、将rdd1每个元素平方得到rdd2任务3、利用映射算子打印菱形IDEA里创建项目实现 七、过滤算子案例任务1、过滤…

编写 ROS 服务节点 Service 和 Client(python/C++)(六)

1.编写 Service 节点&#xff08;C&#xff09; 进入目录 cd ~/catkin_ws/src/beginner_tutorials/src然后vim server.cpp 复制代码粘贴&#xff0c;shiftinsert 粘贴 &#xff0c;然后按Esc 键&#xff0c;然后输入:wq 就可以保存退出了 #include "ros/ros.h" …

OSI分层

1 应用层 最上层的&#xff0c;也是我们能直接接触到的就是应用层&#xff08;Application Layer&#xff09;&#xff0c;我们电脑或手机使用的应用软件都是在应用层实现。那么&#xff0c;当两个不同设备的应用需要通信的时候&#xff0c;应用就把应用数据传给下一层&#x…

小航助学信息学奥赛C++ GoC模拟试卷(含题库答题软件账号)

信息学奥赛C GoC系统请点击 电子学会-全国青少年编程等级考试真题Scratch一级&#xff08;2019年3月&#xff09;在线答题_程序猿下山的博客-CSDN博客_小航答题助手 单选题10.0分 删除编辑 答案:C 第1题goc命令可以通过多命令拼接方式&#xff0c;优化代码布局&#xff0c;…

DHTMLX Suite JS PRO 8.1.1 Crack

适用于现代 Web 应用程序的强大 JavaScript 小部件库 - DHTMLX 套件 用于创建现代用户界面的轻量级、快速且通用的 JavaScript/HTML5 UI 小部件库。 DHTMLX Suite 有助于推进 Web 开发和构建具有丰富功能的数据密集型应用程序。 DHTMLX Suite 是一个 UI 小部件库&#xff0c;用…

用于脑MRI分割的注意对称自动编码器

文章目录 Attentive Symmetric Autoencoder for Brain MRI Segmentation摘要本文方法Attentive Reconstruction Loss位置编码SPE下游任务网络结构 实验结果 Attentive Symmetric Autoencoder for Brain MRI Segmentation 摘要 基于图像块重建的自监督学习方法在训练自动编码器…

深度学习基础入门篇[9.1]:卷积之标准卷积:卷积核/特征图/卷积计算、填充、感受视野、多通道输入输出、卷积优势和应用案例讲解

【深度学习入门到进阶】必看系列&#xff0c;含激活函数、优化策略、损失函数、模型调优、归一化算法、卷积模型、序列模型、预训练模型、对抗神经网络等 专栏详细介绍&#xff1a;【深度学习入门到进阶】必看系列&#xff0c;含激活函数、优化策略、损失函数、模型调优、归一化…

6个AI绘画网站,可生成海报

目录 1、Midjourney 2、Stable Diffusion Omline 3、Microsoft Designer 4、Craiyon 5、NightCafe S 6、Wombo 1、Midjourney 特点&#xff1a;业内标杆&#xff0c;效果最强大 Midjourney是基于diffusion的AI图画艺术生成器。生成图片不局限于二次元人物&#xff0c;能够…

产品经理在空窗期该做什么?

最近一段时间&#xff0c;就业形势越来越严峻&#xff0c;尤其是互联网行业&#xff0c;尤其是产品经理这个岗位竞争更是激烈&#xff0c;很多产品经理都难免有了数个月的空窗期&#xff0c;而空窗期的存在又使得产品经理竞争力下降&#xff0c;形成了负循环。而唯一能打破这种…

FPGA纯verilog代码实现H265视频压缩 支持4K30帧分辨率 提供工程源码和技术支持

这里写目录标题 1、前言2、我这里已有的视频图像编解码方案3、H265--视频压缩理论4、H265--视频压缩--性能表现5、H265--视频压缩--设计方案6、H265--视频压缩--时序7、Vivado工程详解8、移植上板应用9、Vivado功能仿真10、福利&#xff1a;工程代码的获取 1、前言 H265视频压…