Mybatis(六)缓存

news2025/1/16 0:46:59

缓存是Mybatis中非常重要的特性,Mybatis的一级缓存基于SqlSession实现,二级缓存基于Mapper实现。

一、缓存的使用

一级缓存默认开启,Mybatis提供了一个配置参数localCacheScope来控制一级缓存的级别,该参数的取值可以是session、statement等,当取值为session时,缓存对整个sqlsession有效,只有执行DML(更新)语句时缓存才会被清除,当取值为statement是,缓存只对当前执行的语句生效,当语句执行完后缓存会被清空。

Mybatis的一级缓存默认开启,且用户只能改变其级别,而不能进行关闭。所以我们重点看看Mybatis的二级缓存的使用:

二、Mybatis的缓存实现类

Mybatis的缓存基于JVM堆内存来实现的,即所有缓存对象都放在java对象中,并且通过Cache接口定义缓存对象的行为,Cache接口代码如下:

 Mybatis的缓存类使用装饰者模式,Cache接口只有一个基本的实现类,即PrepetualCache类,该类通过一个HasshMap存放缓存对象。需要注意的是,PrepetualCache类重写了Object类的equals方法(所以当两个缓存对象的id一样时(一般缓存对象的Id为mapper.xml中的命名空间名称,即全限定类名),则认为缓存对象相同)。

mybatis还对PerpetualCache类进行了增强,提供了一些缓存的装饰类,具体如下:

 这些装饰类相应的功能如下:

 另外缓存对象的创建是mybatis提供的CacheBuilder类通过生成器模式创建的,例如下面使用该类创建一个缓存对象:

三、一级缓存的实现原理

上面讲的缓存实现类,是mybatis一级缓存、二级缓存的基础,现在我们先来讲下一级缓存的实现。

Mybatis的一级缓存是SqlSession级别的缓存,在介绍核心组件的时候有提过SqlSession提供了面向用户的api,而真正执行sql的是Executor组件,Executor采用模板方法设计模式,BaseExecutor类用于处理一些通用的逻辑,其中一级缓存的逻辑就是在BaseExecutor类中完成的,

接下来我看看主要实现,一级缓存在BaseExecutor类中使用PerpetualCache实例实现的,在该类中维护了两个PerpetualCache属性,代码如下:

 其中localCache用来缓存Mybatis查询的结果,另一个用来缓存存储过程调用的结果。这两个属性在BaseExecutor构造方法中进行初始化的,代码如下:

Mybatis通过CacheKey对象来描述缓存的key值(即存进PerpetualCache实例的是一个key-value结构,可以为CacheKey,value为缓存对象),在执行查询操作时,先创建了Cachekey对象,如果两次查询操作的Cachekey对象相同,则认为这两次查询执行的是相同的SQL语句。CacheKey对象是通过BaseExecutor类的createKey方法创建的,代码如下:

 

 BaseExecutor类的query方法(即查询api)的具体实现:

可以看到,query方法中,先根据key去缓存中获取缓存对象,如果没有则调用queryFromDatabase方法从数据库中获取数据,然后再将数据写入缓存中。

需要注意的是,如果localCacheScope属性设置为statement时,每次查询操作完成后,都会调用clearLocalCache方法清空缓存,另外mybatis也会在每次执行更新操作前清空缓存,具体代码可以看BaseExecutor的update方法:

 可以看到,在调用doUpdate方法前都会先清空缓存。

注意:(这里后面验证一下)

四、Mybatis二级缓存实现原理

默认情况下二级缓存是关闭的,可通过摄者cacheEnabled参数值为true来开启二级缓存,前面说过Sqlsession将执行的逻辑委托给Executor组件完成,而Executor接口有几种不同的实现,分别为SimpleExecutor、BatchExecutor、ReuseExecutor,另外还有一个比较特殊的CachingExecutor,CachingExecutor采用了装饰器模式,在其他Executor的基础上增加了二级缓存功能。

        Executor实例创建是通过工厂模式创建,Configuration类提供一个工厂方法newExecutor方法用来创建返回一个Executor对象,我们可以看看这个方法的实现:

可以看到,Configuration类的newExecutor工厂方法根据defaultExecutorType参数知道的Executor类型创建对应的Executor实例。

如果cacheEnabled属性值为true(开启二级缓存),则使用CachingExecutor对普通的Executor对象进行装饰,CachingExecutor在普通的Executor的基础上增加了二级缓存,我们接下看看CachingExecutor的实现,下面先看看该类的属性信息:

 我们看看这个TransactionCacheManager(用于管理所以的二级缓存对象):

在TransactionalCacheManager类中,通过一个HashMap去维护二级缓存实例对应的 TransactionalCache对象,并提供获取缓存等方法。那么下面我们直接去看CachingExecutor的query实现方法:

 可以看到先调用了createCacheKey 创建缓存key,然后调用MapperStatement对象的getCache获取MapperStatement对象中维护的二级缓存对象(我们知道一个MapperStatement对应一个Mappper接口),然后从二级缓存对象中获取缓存结果,如果获取不到则调用Executor的query方法从数据库获取数据,在将数据添加到二级缓存,如果有更新操作则同一命名空间下的二级缓存会被清空,下面看下CachingExecutor的update方法:

上面我们说维护二级缓存的实例是从MappedStatement中获取的,那我们下面看看创建MapppedStatement是怎么去创建这些二级缓存实例的:

五、二级缓存集成redis

Redis实现Mybatis二级缓存_redis的二级缓存_悠然予夏的博客-CSDN博客

Mybatis的二级缓存、使用Redis做二级缓存

六、一级缓存与二级缓存的区别,以及二级缓存的弊端

  一级缓存的CacheKey是Mapper的命名空间+<select|update|insert|delete>的id组成的,且是基于SqlSession级别进行操作的(一般情况下,当我们使用Mybatis进行数据库的操作时候,会创建一个SqlSession来进行一次数据库的会话,会话结束则关闭SqlSession对象)

注意:两次查询须在同一个sqlsession中完成,否则将不会走mybatis的一级缓存。

在mybatis与spring进行整合开发时,事务控制在service中进行,重复调用两次servcie将不会走一级缓存,因为在第二次调用时session方法结束,SqlSession就关闭了。(同一个事务查询两次才会用到缓存)
(所以一般不用设置为Statement???)

而二级缓存则是基于Mapper级别进行的,即所有SqlSession的缓存进行共享(管理二级缓存额实例是存在MappedStatement的,而一个MappedStatement对应一个Mapper(即同一个命名空间对应一个二级缓存))

 

二级缓存是存在一个问题的,当对一个表的增删改查不在同一个namespace(即同一个接口中)时,会出现脏读现象,因为二级缓存是Mapper级别的(即一个Mapper接口层面),如果同一个表的修改在其他namespace(即其他Mapper)执行,此时第一个Mapper的二级缓存无法感知到第二个Mapper的二级缓存的改变,所以导致读到未刷新的数据。因此我们一般不建议使用二级缓存,如果需要使用二级缓存可以集成第三方缓存例如redis。

Mybatis二级缓存的缺陷_mybatis缓存的坏处_龙域、白泽的博客-CSDN博客

Mybatis一级缓存与二级缓存的区别你知道吗_mybatis的一级缓存和二级缓存的区别_Java小叮当的博客-CSDN博客

Mybatis 中的一级缓存与二级缓存_mybatis一级缓存和二级缓存_头真的好重好重Y的博客-CSDN博客

 

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

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

相关文章

【机器学习】P10 从头到尾实现一个线性回归案例

这里写自定义目录标题&#xff08;1&#xff09;导入数据&#xff08;2&#xff09;画出城市人口与利润图&#xff08;3&#xff09;计算损失值&#xff08;4&#xff09;计算梯度下降&#xff08;5&#xff09;开始训练&#xff08;6&#xff09;画出训练好的模型&#xff08;…

参加Matlab与AI讲座:使用深度强化学习训练走路机器人观后感

时间&#xff1a;2023年4月12日&#xff0c;周三&#xff0c;天气晴 地址&#xff1a;大连理工大学研教楼303 前言&#xff1a;Matlab其实有很多功能&#xff0c;我们所用的只是最基础最简单的部分&#xff0c;例如矩阵计算&#xff0c;画图等等。 随着强化学习的发展&#xff…

一般形式的S曲线公式推导

文章目录一、背景二、目标三、计算3.1 S曲线基本形式3.2 S曲线变换3.3 参数计算3.4 S曲线中心对称条件四、总结五、附件一、背景 S曲线因具备良好可控的平滑性、单调性、连续可导性等优点&#xff0c;常作为各类电机升降速曲线。当前多数S曲线的介绍文章未给出推导过程&#x…

SpringCloud微服务技术栈.黑马跟学(五)

SpringCloud微服务技术栈.黑马跟学 五今日目标1.初识elasticsearch1.1.了解ES1.1.1.elasticsearch的作用1.1.2.ELK技术栈1.1.3.elasticsearch和lucene1.1.4.为什么不是其他搜索技术&#xff1f;1.1.5.总结1.2.倒排索引1.2.1.正向索引1.2.2.倒排索引1.2.3.正向和倒排1.3.es的一些…

SpringMVC基本注解的使用和理解

SpringMVC基本注解的使用和理解 RequestParam注解 使用在方法入参位置&#xff0c;用于指定请求参数名称&#xff0c;将该请求参数绑定到注解参数位置。 属性&#xff1a;name:指定要绑定的请求参数名称&#xff1b; name属性和value属性互为别名。 required 和&#xff1a;指…

Java并发编程(8) —— AQS抽象同步队列详解

上一篇&#xff1a;Java并发编程(7) —— 锁的分类概述 在上一篇中我们提到并发包中的ReentrantLock类是一种可重入独占锁&#xff0c;其锁机制是基于AQS实现的。实际上&#xff0c;并发包java.util.concurrent.locks中的锁都是基于AQS 实现的。 一、AQS是什么 AbstractQueued…

13. unity粒子特效--发射模块、各种发射器形状、粒子渐变(颜色/大小)

1. 发射模块&#xff08;Emission&#xff09; 匀速发射&#xff1a; Rate over Time&#xff1a;每秒钟发射的粒子数 Rate over Distance&#xff1a;每移动一米发射的粒子个数 两者可指定其一&#xff1a;若仅指定Rate over Time&#xff0c;则粒子根据时间的变化进行发射&a…

第三节、语言模型

目录 0、介绍 1、N-gram 模型介绍 2、困惑度 3、N-gram 模型的文本评估 4、N-gram 模型的平滑 5、基于 N-gram 模型的文本生成 6、基于统计的语言模型的缺陷 7、实验总结 0、介绍 首先&#xff0c;我们来思考这样一个问题&#xff1a;随便给你一句话&#xff0c;如何判…

MongoDB中的索引

一、说明 索引通常能够极大的提高查询的效率,如果没有索引,MongoDB在读取数据时必须扫描集合中的每个文件并选取那些符合查询条件的记录。这种扫描全集合的查询效率是非常低的,特别在处理大量的数据时,查询可能要花费几十秒甚至几分钟,这对网站的性能是非常致命的。索引是…

Javaweb小练习---在JSP中使用Javabean访问数据库完成用户信息的简单添加

Javaweb小练习---在JSP中使用Javabean访问数据库完成用户信息的简单添加 目录 Javaweb小练习---在JSP中使用Javabean访问数据库完成用户信息的简单添加 0.创建数据库 1. 在resources目录下创建db.properties文件 2. /** * 获取链接与释放资源的工具类--JdbcUtil类 */ 3…

UE-Ueransim-5GC全链路开发记录

目录 1. 系统配置 1.1 Ueransim配置 1.2 UE配置 2. 启动 3. 实际演示 附录 代理1&#xff1a;ueransim-5gc 代理2 ue-ueransim TCPclient TCPserver 1. 系统配置 1.1 Ueransim配置 ueransim的yaml文件如下 version: 3.8 services:ueransim2:container_name: uera…

Ubantu docker学习笔记(六)容器数据卷

文章目录一、容器数据卷二、容器卷挂载2.1 在命令行挂载数据卷2.2 通过dockerfile挂载数据卷三、数据卷容器四、备份数据卷五、数据卷的恢复和迁移5.1 恢复数据卷5.2 迁移数据卷六、管理数据卷6.1 与容器关联&#xff08;1&#xff09;例子一&#xff08;2&#xff09;例子二&a…

【LeetCode】剑指 Offer(28)

目录 题目&#xff1a;剑指 Offer 54. 二叉搜索树的第k大节点 - 力扣&#xff08;Leetcode&#xff09; 题目的接口&#xff1a; 解题思路&#xff1a; 代码&#xff1a; 过啦&#xff01;&#xff01;&#xff01; 题目&#xff1a;剑指 Offer 55 - I. 二叉树的深度 - 力…

MySQL运维11-MySQL的事务隔离级别

文章目录1、MySQL的事务隔离级别2、MySQL事务隔离级别的相关参数和命令2.1、查看事务隔离级别2.2、设置事务隔离级别2.2.1、在会话中设置事务隔离级别2.2.2、在配置文件中设置事务隔离级别3、MySQL的多版本并发控制(MVCC)4、总结1、MySQL的事务隔离级别 事务隔离级别越高&…

Qt5.12实战之规则DLL导出函数使用

1.创建基于MFC的规则DLL工程: 输入工程名,然后点击创建 选择使用共享MFC DLL的常规DLL 创建成功后,解决方案下会多出一个工程 增加导出函数声明 实现导出函数 在模块定义文件def文件中声明导出

YC-B09(原创)基于springboot,vue网上书城

(原创)基于springboot,vue网上书城定制版v4.0 本人原创作品&#xff0c;用户前台、系统管理员后台项目完整&#xff0c;无任何bug。 每行代码都是本人自己写&#xff0c;我在代码上面都写有详细注释 开发工具&#xff1a;IDEA 服务器&#xff1a;Tomcat9.0&#xff0c; jdk…

代码随想录算法训练营第五十九天-单调栈2| 503.下一个更大元素II 42. 接雨水

503. Next Greater Element II 成环就用取模mod方法 import java.util.Arrays; import java.util.Stack;public class NextGreaterElement2 {public int[] nextGreaterElements(int[] nums) {//边界判断if(nums null || nums.length < 1){return new int[]{-1};}int size …

无需兔魔法!国内手机直接畅玩GPT!

为了用上ChatGPT很多同学都是经历一波三折&#xff0c;闯三关过五将&#xff01;因为使用ChatGPT的门槛很高&#xff0c;尤其是这个kx上网把很多人都挡在了门外&#xff01;有的同学说newbing呢&#xff0c;newbing如果你要用聊天功能&#xff0c;一样有这样门槛&#xff01;很…

Ubuntu docker 基本操作

安装docker&#xff1a; curl -fsSL https://get.docker.com | bash -s docker --mirror Aliyun 也可以使用国内 daocloud 一键安装命令&#xff1a; curl -sSL https://get.daocloud.io/docker | sh 启动Docker命令&#xff1a; systemctl start docker 查看版本号&#xf…

NKCTF2023 babyrust

这道题目适合科普&#xff1a;rust逆向&#xff0c;xmm指令。 rust逆向的一些注意事项 rust题中&#xff0c;“ida给的main”里的第一个lea是“用户写的main”。 如下图&#xff0c;这是ida标记的main。而用户写的main其实是sub_1400012A0。姑且可以认为“ida给的main”里的…