大厂Java面试题:详细描述MyBatis缓存的实现原理

大家好,我是王有志。今天给大家带来的是一道来自光大科技的 MyBatis 面试题:详细描述MyBatis缓存的实现原理。

在通过源码分析 MyBatis 一二级缓存的实现原理前,我先给出我的回答。

首先是 MyBatis 一级缓存的实现原理:

MyBaits 的一级缓存是默认开启的,作用域是 SqlSession 实例,所以 MyBatis 一级缓存在 SqlSession 之间是隔离的。MyBatis 应用程序中,每个 SqlSession 实例都会持有 Execuutor 实例,而 Executor 实例中又会持有 Cache 实例,这个 Cache 实例就是 MyBatis 的一级缓存。因为每个 SqlSession 实例是相互独立且隔离的,因此 Cache 实例在 SqlSession 之间也是相互独立且隔离的。

接着来看 MyBatis 二级缓存的实现原理:

MyBatis 的二级缓存是默认关闭的,作用域是 SqlSessionFactory 实例,所以 MyBatis 二级缓存在 SqlSesiion 之间是共享的。MyBatis 应用程序在启动时,解析 MyBatis 的核心配置文件 mybatis-config.xmk 生成 Configuration 实例,当解析到 XML 映射器时,会先根据 XML 映射器的配置创建 MyBatis 二级缓存的 Cache 实例,在之后解析 XML 映射器的每个 SQL 语句并生成相应的 MappedStatement 实例时,会将 MappedStatement 实例中的 cache 指向创建的 Cache 实例,并将所有根据 XML 映射器配置生成的 Cache 实例存储到 Configuration 实例的 caches 中。因为 MappedStatement 实例是按照每个 XML 映射器的 namesapce 划分的,因此我们会说 MyBatis 二级缓存的作用域是 XML 映射器的 namesapce,有因为 MappedStatement 实例被 Configuration 实例持有,且 Configuration 实例被 SqlSessionFactory 实例持有,我们也可以说 MyBatis 二级缓存的作用域是 SqlSessionFactory。

下面我们来通过源码分析 MyBatis 一二级缓存的实现原理。

原理分析

我们先通过一张图来构建出 MyBatis 中一二级缓存与 MyBatis 各个组件之间的关系,如下所示:

MyBatis缓存结构.png

上图中,我只画出了 MyBatis 应用中与缓存相关的组件,这样能够帮助我们排除其它组件的干扰,清晰的了解到 MyBatis 的缓存与各组件之间的关系。接下来我们通过源码的角度来简单剖析 MyBatis 缓存的实现原理。

Tips:我在掘金专栏中的两篇文章《MyBatis中一级缓存的配置与实现原理》和《MyBatis中二级缓存的配置与实现原理》有更加详细的源码分析,感兴趣的可以去看看。

MyBatis 的一级缓存

从上图中,我们已经可以看到 MyBatis 的一级缓存位于 SqlSession 实例持有的 Executor 实例中,那么我们就从 MyBatis 中获取 SqlSession 实例的源码入手:

MyBatis创建一级缓存.jpg

我们从DefaultSqlSessionFactory#openSession方法入手,通过追踪源码的调用逻辑可以得知,MyBatis 的一级缓存实际上是由 Executor 的抽象父类 BaseExecutor 创建的,由于每个 SqlSession 都会持有 Executor 实例,因此每个 SqlSession 都会创建 MyBatis 一级缓存,所以在创建时 MyBatis 的一级缓存就已经想相互隔离了。

接着我们来看 MyBatis 一级缓存的使用,我们直接来看BaseExecutor#query方法,部分源码如下:

public abstract class BaseExecutor implements Executor {
  public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
    List<E> list;
    try {
      queryStack++;
      list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
      if (list != null) {
        handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
      } else {
        list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
      }
    } finally {
      queryStack--;
    }
    if (queryStack == 0) {
      for (DeferredLoad deferredLoad : deferredLoads) {
        deferredLoad.load();
      }
      deferredLoads.clear();
      if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
        clearLocalCache();
      }
    }
    return list;
  }
}

第 6 行源码中,通过 BaseExecutor 的 localCache 获取数据,如果获取成功执行第 8 行的BaseExecutor#handleLocallyCachedOutputParameters方法处理数据,否则执行第 10 行的BaseExecutor#queryFromDatabase方法,通过数据库获取数据,该方法的部分源码如下:

public abstract class BaseExecutor implements Executor {
  private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
    List<E> list;
    localCache.putObject(key, EXECUTION_PLACEHOLDER);
    try {
      list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
    } finally {
      localCache.removeObject(key);
    }
    localCache.putObject(key, list);
    if (ms.getStatementType() == StatementType.CALLABLE) {
      localOutputParameterCache.putObject(key, parameter);
    }
    return list;
  }
}

第 6 行代码执行的BaseExecutor#doQuery方法是抽象方法,由 BaseExecutor 子类实现,主要的功能是实现通过数据库查询数据,第 10 行代码中将查询出的数据存入到 localCache 中。

MyBatis 的二级缓存

相较于 MyBatis 的一级缓存,MyBatis 二级缓存的逻辑就复杂了很多,我们先来看 MyBatis 二级缓存的创建逻辑。

从 MyBatis 缓存与 MyBatis 组件之间的关系图中可以看到,MyBatis 的二级缓存存储在 Configuration 实例中,同时每个 MappedStatement 实例中也持有指向 MyBatis 二级缓存的引用,那我们就从创建 Configuration 实例和创建 MappedStatement 实例的源码入手,如下所示:

MyBatis创建二级缓存.jpg

MyBatis 创建二级缓存的逻辑会复杂很多,简单总结一下就是在创建 MyBatiis 运行环境 SqlSessionFactory 时,通过解析 XML 映射器文件配置创建缓存,将缓存添加到 Configuration 实例中,并且在每个 XML 映射器中的 MappedStatement 实例中保存指向缓存的引用

因为 MyBatis 中 SqlSession 实例是通过 SqlSessionFactory 获取的,使用 SqlSession 实例执行 SQL 语句时,会通过 SqlSessionFactory 实例获取对应的 MappedStatement 实例,而 MappedStatement 实例在不同的 SqlSession 之间共享,因此 MappedStatement 中持有的 MyBatis 二级缓存在不同的 SqlSession 实例之间也是共享的。

Tiips:将缓存添加到 Configuration 实例中源码在MapperBuilderAssistant#useNewCache方法中的第 7 行。

接着我们来看 MyBatis 二级缓存的使用,与 MyBatis 一级缓存不同的是,在使用 MyBatis 二级缓存时,创建的 Executor 类型是 CachingExecutor,我们直接来看CachingExecutor#query方法,源码如下:

public class CachingExecutor implements Executor {
  public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
    Cache cache = ms.getCache();
    if (cache != null) {
      flushCacheIfRequired(ms);
      if (ms.isUseCache() && resultHandler == null) {
        ensureNoOutParams(ms, boundSql);
        @SuppressWarnings("unchecked")
        List<E> list = (List<E>) tcm.getObject(cache, key);
        if (list == null) {
          list = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
          tcm.putObject(cache, key, list);
        }
        return list;
      }
    }
    return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
  }
}

第 3 行源码中,通过 MappedStatement 实例获取 Cache 实例,第 9 行源码中,通过 Cache 实例获取数据,如果获取到数据则直接返回,否则执行第 11 行代码,通过数据库查询数据。

MyBatis 中使用一级缓存和二级缓存的逻辑都非常简单,重点在于 MyBatis 一级缓存和二级缓存的创建,MyBatis 一级缓存的创建逻辑比较简单,但是创建 MyBatis 二级缓存的逻辑会比较复杂,大家可以通过源码并结合最开始给出的结构图来理解 MyBatis 二级缓存的创建过程。


尾图(二维码).png

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

vscode插件开发之 - menu配置

上一遍博客介绍了如何从0到1搭建vscode插件开发的base code&#xff0c;这遍博客将重点介绍如何配置menu。通常&#xff0c;开发一款插件&#xff0c;会将插件显示在VSCode 左侧的活动栏&#xff08;Activity Bar&#xff09;&#xff0c;那么如何配置让插件显示在Activity Bar…

[Shell编程学习路线]——深入理解Shell编程中的变量(理论与实例)

&#x1f3e1;作者主页&#xff1a;点击&#xff01; &#x1f6e0;️Shell编程专栏&#xff1a;点击&#xff01; ⏰️创作时间&#xff1a;2024年6月12日11点40分 &#x1f004;️文章质量&#xff1a;95分 文章目录 ————前言———— 1 自定义变量 &#x1fae0;…

数据结构笔记39-48

碎碎念&#xff1a;想了很久&#xff0c;不知道数据结构这个科目最终该以什么笔记方式呈现出来&#xff0c;是纸质版还是电子版&#xff1f;后来想了又想&#xff0c;还是电子版吧&#xff1f;毕竟和计算机有关~&#xff08;啊哈哈哈哈哈哈哈&#xff09; 概率论已经更新完了&…

为国产加油:“缺芯少屏”暂缓,另一领域,也要加把劲

说起咱中国之前的“缺芯少屏”&#xff0c;真的是让人挺闹心的。 不过呢&#xff0c;为了改变这个状况&#xff0c;咱们的工程师们可是费了不少劲儿&#xff0c;辛辛苦苦努力了数十年。现在好了&#xff0c;咱们也迎来了柔性屏的时代。 柔性屏 说起来&#xff0c;在触摸屏或者…

消费者消费数据时报错:INVALID_REPLICATION_FACTOR

今天部署了kafka集群&#xff0c;三台服务器&#xff0c;启动后&#xff0c;生产者发送数据&#xff0c;消费者接收数据的时候报错&#xff0c;INVALID_REPLICATION_FACTOR。 查了很多资料&#xff0c;说是要改kafka下config目录的server.properties,可能是副本数太小&#xff…

【MATLAB源码-第225期】基于matlab的计算器GUI设计仿真,能够实现基础运算,三角函数以及幂运算。

操作环境&#xff1a; MATLAB 2022a 1、算法描述 界面布局 计算器界面的主要元素分为几大部分&#xff1a;显示屏、功能按钮、数字按钮和操作符按钮。 显示屏 显示屏&#xff08;Edit Text&#xff09;&#xff1a;位于界面顶部中央&#xff0c;用于显示用户输入的表达式和…

Python学习打卡:day05

day5 笔记来源于&#xff1a;黑马程序员python教程&#xff0c;8天python从入门到精通&#xff0c;学python看这套就够了 目录 day538、函数的初体验39、函数的基础定义语法函数的定义注意事项 40、函数的基础定义案例练习41、函数的传入参数42、函数的传入参数案例练习——升…

python django初步搭建(一)

记录一次简单的python django使用&#xff0c;后续调用api相关的暂时不想写。。。 一、环境 windows python 3.11.7 django 二、初步搭建 2.1 新建空文件夹 为了方便本次记录&#xff0c;新建了一个空的文件夹来使用。 直接在这里输入cmd 然后按下回车 2.2 安装virtual…

Kubernetes集群持久化部署实践

WordPress 网站持久化部署 要持久化MariaDB 可以把 Deployment 改成了 StatefulSet&#xff0c;修改 YAML添加“serviceName”“volumeClaimTemplates”这两个字段&#xff0c;定义网络标识和 NFS 动态存储卷&#xff0c;然后在容器部分用“volumeMounts”挂载到容器里的数据目…

利用three-csg-ts对做物体交互式挖洞

默认物体均为居中&#xff0c;如果指定位置没有发生偏移&#xff0c;可能是因为在执行布尔操作之前没有正确设置变换。确保在进行布尔运算之前应用所有必要的变换。以下是经过修正的完整代码示例&#xff0c;它会确保圆柱正确旋转并与盒子进行 CSG 操作。 安装依赖 首先&…

快捷回复话术分享:如何应对顾客愤怒骂人?

在客服的日常工作中&#xff0c;面对情绪激动、甚至愤怒发泄骂人的顾客是常见的挑战。初入此行业的小伙伴们往往在遭遇顾客的激烈情绪时感到手足无措&#xff0c;不知道如何妥善回应。为此&#xff0c;本文将分享一些实用的快捷回复话术和技巧&#xff0c;帮助新手客服更好地处…

vue聊天发送Emoji表情

在用web端写聊天发送表情的功能中&#xff0c;使用web端有系统自带的unicode表情会出现每端不统一的情况&#xff0c;不好用不能统一&#xff0c;在这里我想到了一个非常好的思路&#xff0c;可以解决这个问题&#xff01; 那就是发送表情用图片的形式呈现&#xff0c;然后发给…

电脑屏幕怎么显示提醒事项 电脑桌面提醒事项设置

在这个信息爆炸的时代&#xff0c;我们每个人都像是被数据洪流裹挟着前进。工作中&#xff0c;生活中&#xff0c;无数琐碎而重要的事情需要我们记忆和处理。有时&#xff0c;仅仅依靠大脑去记住所有事情&#xff0c;真的让人头疼。特别是对于那些整日面对电脑的办公族来说&…

Python基础教程(十一):数据结构汇总梳理

&#x1f49d;&#x1f49d;&#x1f49d;首先&#xff0c;欢迎各位来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里不仅可以有所收获&#xff0c;同时也能感受到一份轻松欢乐的氛围&#xff0c;祝你生活愉快&#xff01; &#x1f49d;&#x1f49…

使用C#快速搭建一个在windows运行的exe应用

文章目录 一、前言1.1 编写语言需要工具1.2 选择自己需要的组件进行安装 二、新建项目1.1 新建一个 .NET4.x 的项目1.2 添加一个小案例1.3 对界面进行美化1.3.1、配置Form属性1.3.2、配置Button按钮 1.4 查看组将的相关代码 三、后记 一、前言 这是一个比较旧的内容&#xff0…

java调用GDAL及JTS实现生成泰森多边形(Voronoi图)的一种方法

目录 一、关于泰森多边形 1.泰森多边形的特性 2.本文的目的 二、实现思路 1.gdal和jts库的maven坐标 2.jts生成泰森多边形的关键代码 3.使用GDAL读取源文件信息的关键代码 4.使用GDAL将生成的泰森多边形写入文件 三、实现结果 1.实现的效果 2.完整代码示例 一、关于…

【STM32】飞控设计

【一些入门知识】 1.飞行原理 【垂直运动】 当 mg&#xff1e;F1F2F3F4&#xff0c;此时做下降加速飞行 当 mg&#xff1c;F1F2F3F4&#xff0c;此时做升高加速飞行 当 mgF1F2F3F4 &#xff0c;此时垂直上保持匀速飞行。 【偏航飞行】 ω 4 ω 2 ≠ ω 1 ω 3 就会产生水…

【CT】LeetCode手撕—200. 岛屿数量

目录 题目1-思路2- 实现⭐200. 岛屿数量——题解思路 3- ACM实现 题目 原题连接&#xff1a;200. 岛屿数量 1-思路 利用 dfs 深搜&#xff0c;遇到岛屿直接将岛屿填充为 0 2- 实现 ⭐200. 岛屿数量——题解思路 class Solution {public int numIslands(char[][] grid) {int …

开源WebGIS全流程常用技术栈

1 数据生产 1.1 uDig uDig&#xff08;http://udig.refractions.net/&#xff09;是一个基于Java开源的桌面应用框架&#xff0c;它构建在Eclipse RCP和GeoTools&#xff08;一个开源的Java GIS包)上。可以进行shp格式地图文件的编辑和查看&#xff1b;是一个开源空间数据查看…

5月产品更新 | 10大更新汇总,快来看看你的需求上线了吗?

5月&#xff0c;Smartbi从客户需求出发&#xff0c;并结合企业在数据分析、处理等方面遇到的问题&#xff0c;对数据模型、数据指标等数十项功能进行了优化升级。 Smartbi用户可以在官网下载下载PC端&#xff0c;更新后便可以使用相关功能&#xff0c;也可以在体验中心体验相关…