MyBatis源码--04:MyBatis缓存实现

news2024/11/24 6:57:59

缓存作用:

在程序访问数据库这个过程中,存在几个性能瓶颈:

  1. 网络通信
  2. 非关系型数据库将数据存储在硬盘当中,需要进行硬盘操作
  3. Java对象复用问题(Connection连接池,Statement对象)

缓存在程序和数据库之间搭建起一个桥梁,把数据存储在内存当中,提高查询效率,避免进行硬盘查询操作(空间换取时间)。

MyBatis实现缓存主要有俩种方式:

  1. ORM框架本身实现:优点:操作快,避免网络通信 缺点:获取的内存大小有限
  2. 第三方缓存系统(比如Redis):优点:缓存空间大 缺点:网络开销更高

内存的空间毕竟有限,空间不够时就要考虑把之前的数据拿出来,这个过程成为 换出。

换出 主要有俩种方式:二进制序列换出和JSON形式换出

换出 主要有俩种算法:LRU(最久未被使用)和FIFO(先进先出)

缓存这个机制应该怎么去实现?

现在考虑一个问题:现在如果让我们实现MyBatis的缓存,应该怎么进行实现?

一个基本的思路是在Dao层先进行缓存操作再去进行数据库操作。比如查询操作,先去缓存中查看是否有目标数据,如果有直接返回,如果没有再去数据库中进行查询。

上面这种方式有一个不能接收的缺点:冗余。除了增加开发量之外,如果后续对缓存进行更改,冗余会产生一个耦合问题。

再对上面的场景进行分析:我们为MyBatis添加缓存-》实际上是一个提高性能的额外操作-》为原始功能增加额外功能-》代理设计模式

实际上这是一个经典的代理设计模式的使用场景:为原始类增加额外功能。

所以我们基于代理设计模式对上面的过程进行优化:

使用AOP自定义模拟实现Cache

Dao接口:

public interface UserDao {
    public void login(User user);

    @Cache//使用Cache注解表明执行前先去缓存
    public void queryAllUsers();
}

Impl:

public class UserDaoService implements UserDao{
    @Override
    public void login(User user) {
        //这里为了方便就不去调用数据库了
        System.out.println("执行login方法");
    }
}

自定义一个注解表明这个sql是否去查缓存

/**
 * @Author 86153
 * @Description 注解。加上注解的方法需要使用Cache,否则不进行使用
 * @Date 12:58 2024/6/7
 * @Param
 * @return
 **/


@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Cache {
}

Aop:

public class MyBatisTest {
    @Test
    public void MyBatisCache() {
        UserDao userDao = new UserDaoService();
        UserDao userDaoProxy = (UserDao) Proxy.newProxyInstance(Test.class.getClassLoader(), new Class[]{UserDao.class}, new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                Cache cache = method.getDeclaredAnnotation(Cache.class);
                if(cache != null) {
                    System.out.println("连接Redis查看是否存在缓存");
                    return method.invoke(userDao,args);
                }
                return method.invoke(userDao,args);
            }
        });
        System.out.println("---------------------------------");
        userDaoProxy.queryAllUsers();
    }

}

结果:

 MyBatis实现Cache的方式:

Cache接口:

MyBatis实现了一个Cache接口:

对于Cache接口的实现方式:

实际上上面的实现方式包括俩大类:PerpetualCache为核心实现类,其它类为核心类的装饰器,用来增强核心类PerpetualCache的功能。

PerpetualCache使用HashMap来放置缓存,HashMap的key就是刚才提到的Cache类getId方法生成的唯一标识符。

装饰器可以套娃使用:

PerpetualCache使用HashMap结构来操作缓存。

Cache的工作过程:

MyBatis二层缓存体系:

在我们进行开发时,SqlSession的更换一定是频繁的。因为SqlSession和Connection对象控制着事务,不能被多用户,多请求共享。

所以一级缓存对于我们的开发的作用是有限的。

一级缓存分析(Executor的适配器设计模式):

我们之前提到过Executor是整个MyBatis进行数据库操作的底层实现类。更具体来说,MyBatis在这里使用了适配器模式:

BaseExecutor作为Executor接口的适配器,把Executor中一些基础功能比如一级缓存进行了实现,其它需要根据实际情况进行实现的方法交给它的子类(上图三个Executor核心类),BaseExecutor把这些方法规定为抽象方法,所以BaseExecutor定义为抽象类。

由此BaseExecutor的所有子类都不用再进行一级缓存的实现。

我们来看看BaseExecutor的query查询方法;

上面的localCache就是Cache的核心功能实现类PerpetualCache.

也因为一级缓存依附于Executor,而不同SqlSession使用不同的Executor,所以一级缓存更换SqlSession后就失效。

同一个sql查询语句,在不同的Mapper文件,它们在一级缓存中产生的key不一样所以也是不能利用的,即一级缓存不能跨Mapper。

那么这里AOP设计模式体现在哪里呢?事实上,我们在使用Dao接口实现类进行数据库操作时就是使用了AOP设计模式。MyBatis使用AOP设计模式底层调用了sqlsession的各种数据库操作,而sqlsession底层又是调用Executor:详情:MyBatis源码分析01 ---- 通过代理确定Mapper接口实现类过程-CSDN博客

二级缓存实现(CachingExecutor):

由于一级缓存是一个基础功能,MyBatis默认开启,所以一级缓存在适配器BaseExecutor中实现。而二级缓存是需要自己开启的,不属于基础功能。所以二级缓存单独通过CachingExecutor进行了实现。

具体来说,CachingExecutor作为一个装饰器对SimpleExecutor(负责基础数据库操作的Executor的实现类)针对缓存功能进行了增强。

验证:在之前的博客中我已经阐述了Executor是Configuration创建的,我们在Configuration中找到相关代码

可以看到Configuration在创建Executor时会判断cacheEbabled是否配置为true,进而确定是否对Executor进行缓存功能的增强。

接下来我们通过CachingExecutor的query查询方法由点及面研究一下CachingExecutor是如何增强核心Executor的缓存功能:

从上面的过程中我们也可以验证开启二级缓存四个条件中的三个:包括cache标签,cacheEnabled配置,useCache配置。(第四个条件是存在事务)。详情:MyBatis二级缓存开启条件-CSDN博客

CachingExecutor的update方法:

直接执行了清空缓存操作,避免脏数据

二级缓存Cache的创建时机和创建方法:

我们之前讲解过在创建sqlSessionFactory时MyBatis进行了xml配置文件的解析(OXM),Cache就是在这个过程中进行创建的:

具体构建过程:

总结:在进行创建SqlSessionFactoryBuilder时我们需要创建Confighration对象,进而我们需要解析俩个XML配置文件,在解析Mapper.xml文件时我们通过判断<cache/>标签进而创建二级缓存,创建完成后放入MappedStatement进而封装到Configuration中(所以之前CachingExecutor的query方法是从MappedStatement中拿到Cache的)。

MyBatis是先查二级缓存还是先查一级缓存

如果我们开启了二级缓存,那么Configuratuon在创建Executor时给我们返回的是一个CachingExecutor。

进而我们在来看看CachingExecutor的查询方法:

可以看到MyBatis先去查了二级缓存,二级缓存没查到时才去一级缓存查询。所以二级缓存也叫全局缓存。

MyBatis二级缓存如何针对每个查询sql放置缓存

在MyBatis创建Configuration对象进行xml文件解析时,MyBatis按照<cache/>(Mapper级别)标签创建PerpeturalCache即二级缓存的HashMap,每个 Mapper.xml 对应的命名空间都拥有自己的二级缓存,这意味着不同 Mapper.xml 文件之间的缓存是隔离的。同一个Mapper文件下的sql的缓存放在一个HashMap中,Key值为namespace.id,Value为一个List放置缓存内容。

验证:

我现在在俩个Mapper文件中设置同一个sql语句,分别调用他们的方法使产生相同的sql,最后发现它们将缓存放在不同的HahMap中:

缓存脏数据问题

MyBatis会在执行增删改操作时清空缓存:

在开启二级缓存后,MyBatis会在事务提交时即commit时清空以及和二级缓存:

清空一级缓存:

清空二级缓存:

如果没有开启二级缓存则会在BaseExecutor执行增删改操时清空一级缓存:

<cache-ref namespace=""/>标签作用:

<cache-ref namespace=""/>使不同Mapper文件利用同一个缓存。

经典使用场景:

        当我们进行联表操作时,会在当前namespace的缓存里存储俩个实体类的数据。比如下面的sql语句会在UserDaoMapper.xml缓存里存储User和Order俩个实体类的数据,这个时候就会出现问题:如果我在OderDaoMapper.xml里更新了Oder表内容,那么在UserDaoMapper的缓存里就会出现脏数据。

解决方法:使用<cache-ref namespace=""/>是俩个Mapper公用同一个缓存,这样任意一个Mapper进行更新时就会清空缓存,杜绝了脏数据的发生。

        

但是联表可能出现脏数据直接清除缓存有点得不偿失,另一种做法是重写相关的清除缓存的接口只去清涉及到联表会产生脏数据的缓存。具体方法这里不再叙述。

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

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

相关文章

Python猫周刊赠书规则与书单

△△请给“Python猫”加星标 &#xff0c;以免错过文章推送 你好&#xff0c;我是猫哥。在创作《Python潮流周刊》一年多的时间里&#xff0c;我已累计通过抽奖赠书 80 本&#xff01;早期周刊是免费分享&#xff0c;所以参与人数很多&#xff0c;现在周刊转为付费后&#xff0…

Vue50-mixin混入

一、为什么要使用 mixin混入 两个组件共享一个配置。 二、使用 mixin混入 2-1、创建一个混合js文件 2-2、引入混合js文件 1、局部混合 在每个组件中都引入混合js文件 注意&#xff1a; 混合就是复用配置&#xff0c;vm实例中的所有的配置项&#xff0c;都能在混合.js文件中写…

springboot与flowable(10):网关服务(排他网关)

一、绘制流程图 排他网关用于对流程中的决策建模。当执行到这个网关时&#xff0c;会按照所有出口顺序流定义的顺序对它们进行计算。选择第一个条件为true的顺序流继续流程。例如员工请假时&#xff0c;小于等于3天由组长审批&#xff0c;大于3天由总监审批。流程案例&#xff…

前端:鼠标点击实现高亮特效

一、实现思路 获取鼠标点击位置 通过鼠标点击位置设置高亮裁剪动画 二、效果展示 三、按钮组件代码 <template><buttonclass"blueBut"click"clickHandler":style"{backgroundColor: clickBut ? rgb(31, 67, 117) : rgb(128, 128, 128),…

0614,表达式,语句

题目一&#xff1a; 许多简单的交互式程序都是基于菜单的&#xff1a;它们向用户显示可供选择的命令列表&#xff1b;一旦用户选择了某条命令&#xff0c;程序就执行相应的操作&#xff0c;然后提示用户输入下一条命令&#xff1b;这个过程一直会持续到用户选择 "退出&qu…

SqlSugar使用DbFirst对象根据数据库表结构创建实体类-C#

本文所述开发环境&#xff1a;.C#、NET8、Visual Studio2022 1. 在项目中安装SqlSugar 在Visual Studio2022中新建一个 C# 的控制台应用程序&#xff0c;框架选择 .Net8。新建后如下图所示&#xff1a; 然后打开NuGet程序包管理器 搜索 SqlSugarCore 并安装 安装后在解决方案…

微服务开发与实战Day09 - Elasticsearch

一、DSL查询 Elasticsearch提供了DSL&#xff08;Domain Specific Language&#xff09;查询&#xff0c;就是以JSON格式来定义查询条件。类似这样&#xff1a; DSL查询可以分为两大类&#xff1a; 叶子查询&#xff08;Leaf query clauses&#xff09;&#xff1a;一般是在特…

【C++提高编程-06】----C++之STL-函数对象、谓词、仿函数

&#x1f3a9; 欢迎来到技术探索的奇幻世界&#x1f468;‍&#x1f4bb; &#x1f4dc; 个人主页&#xff1a;一伦明悦-CSDN博客 ✍&#x1f3fb; 作者简介&#xff1a; C软件开发、Python机器学习爱好者 &#x1f5e3;️ 互动与支持&#xff1a;&#x1f4ac;评论 &…

表面声波滤波器——SAW 基本介绍(1)

声表面波特点与应用 声表面波&#xff0c;也称为表面声波&#xff08;surface acoustic wave&#xff09;&#xff0c;是指在弹性体的自由表面上产生并沿着表面或界面传播的各种模式的波&#xff0c;包括瑞利波(Rayleighwave)&#xff0c;勒夫波(Lovewave)等。 具有以下特点:…

快消品经销商需要注意哪些仓库管理细节,才能提效降本

仓库管理是快消品经销商必须重视的环节&#xff0c;只有仓库管理做好了&#xff0c;整体效率才能得到提升&#xff0c;成本才能降低&#xff0c;客户订单更快地交付&#xff0c;而在仓库管理中有很多细节需要经销商注意。 01仓库布局与设计&#xff1a;合理的仓库布局可以极大地…

13.泛型、trait和生命周期(下)

目录 6. 生命周期与引用有效性6.1 生命周期引入6.2 得到长度最大的String值6.3 生命周期标注语法1&#xff09;说明2&#xff09;普通标注示例3&#xff09;函数参数中的生命周期标注 6.4 深入理解生命周期6.5 结构体定义中的生命周期标注6.6 生命周期省略 6.7 方法定义中的生命…

AI大模型技术揭秘-参数,Token,上下文和温度

深入理解 AI 大模型:参数、Token、上下文窗口、上下文长度和温度 人工智能技术的飞速发展使AI大模型大放异彩,其中涉及的“参数”、“Token”、“上下文窗口”、“上下文长度”及“温度”等专业术语备受瞩目。这些术语背后究竟蕴含何意?它们如何影响AI大模型的性能?一起揭开…

[SWPUCTF 2022 新生赛]善哉善哉(隐写,新佛曰,MD5)

题目&#xff1a; 我们看到&#xff1a;题目就是一张图片便联想到隐写术。、 首先查看图片的详细信息我们看到 又看到有关MD5加密。 我们利用小鲨鱼(Stegsolve)破解得到&#xff1a; 小鲨鱼下载可参考&#xff1a;CSDN小鲨鱼下载安装配置 最后面有一段摩斯密码&#xff0c;破…

Explain Python Machine Learning Models with SHAP Library

Explain Python Machine Learning Models with SHAP Library – Minimatech &#xff08;能翻墙直接看原文&#xff09; Explain Python Machine Learning Models with SHAP Library 11 September 2021Muhammad FawiMachine Learning Using SHapley Additive exPlainations …

Elixir学习笔记——输入输出和文件系统

本章介绍输入/输出机制、文件系统相关任务以及相关模块&#xff08;如 IO、File 和 Path&#xff09;。IO 系统提供了一个很好的机会来阐明 Elixir 和 Erlang VM 的一些思维模式和新奇思想。 输入输出模块 输入输出模块是 Elixir 中读写标准输入/输出 (:stdio)、标准错误 (:s…

HTML表格的跨行与跨列:《红楼梦》人物与小学课表示例

在HTML中&#xff0c;表格不仅可以按常规行和列排列数据&#xff0c;还可以通过跨行&#xff08;rowspan&#xff09;和跨列&#xff08;colspan&#xff09;属性来合并单元格&#xff0c;以适应更复杂的数据展示需求。以下是跨行与跨列属性的介绍&#xff0c;以及两个示例&…

centos环境上:k8s 简单安装教程

本次演示安装3节点k8s环境&#xff0c;无需多言&#xff0c;直接上操作步骤&#xff1a; 1、环境准备 k8s部署前&#xff0c;首先需要准备好环境&#xff0c;除了1.4 步骤&#xff0c;其他步骤在所有&#xff08;3个&#xff09;节点上都要执行&#xff1a; 1.1 关闭防火墙 s…

移动硬盘数据恢复方法哪个好?六个硬盘恢复,新手也能用!

移动硬盘数据恢复方法哪个好&#xff1f;移动硬盘&#xff0c;作为我们存储重要数据的常用设备&#xff0c;一旦里面的视频、文档、音频等资料突然消失&#xff0c;确实会令人烦恼和担忧。然而&#xff0c;因为数据丢失的原因可能多种多样&#xff0c;因此恢复方法也会有所不同…

ABBYY Finereader 15软件下载及安装教程

ABBYY FineReader 是一款功能强大的 OCR&#xff08;Optical Character Recognition&#xff09;软件&#xff0c;可以将扫描的文档转换为可编辑的文本文件。它不仅可以识别文本&#xff0c;还可以识别表格、图像和布局&#xff0c;使得文档的转换更加准确和方便。 安 装 包 获…

【计算机毕业设计】基于Springboot的车辆管理系统【源码+lw+部署文档】

包含论文源码的压缩包较大&#xff0c;请私信或者加我的绿色小软件获取 免责声明&#xff1a;资料部分来源于合法的互联网渠道收集和整理&#xff0c;部分自己学习积累成果&#xff0c;供大家学习参考与交流。收取的费用仅用于收集和整理资料耗费时间的酬劳。 本人尊重原创作者…