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

news2024/10/5 14:40:57

大家好,我是王有志。今天给大家带来的是一道来自光大科技的 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.coloradmin.cn/o/1816486.html

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

相关文章

Clion控制台打印中文乱码

第一步 第二步 第三步 ctrlaltshift/ 把run.processes,with.pty

文字悬停效果

文字悬停效果 效果展示 CSS 知识点 CSS 变量使用回顾-webkit-text-stroke 属性的运用与回顾 页面整体结构实现 <ul><li style"--clr: #e6444f"><a href"#" class"text">First</a></li><li style"--cl…

NiceGUI:让Python变身为Web应用开发大师的神器

简介 NiceGUI是一个易于使用的基于Python的UI框架&#xff0c;可以在您的Web浏览器中使用。您可以创建按钮、对话框、Markdown、3D场景、图表等等。 NiceGUI开源支持较好&#xff0c;代码更新频率较高&#xff0c;目前已经更新至: V1.4.26。 适用场景 NiceGUI非常适用于各种…

端点物联开发教程之(一)什么是端点物联

目录 一、手机端演示 二、开发套件 三、嵌入式端 四、平台端 五、手机端 本项目的交流QQ群:701889554 物联网实战--入门篇https://blog.csdn.net/ypp240124016/category_12609773.html 物联网实战--驱动篇https://blog.csdn.net/ypp240124016/category_12631333.html 物…

BL104钡铼多协议采集网关助力企业智能化转型

BL104钡铼多协议采集网关&#xff08;PLC物联网关BL104&#xff09;是为满足工业环境需求而设计的专业工业级协议转换网关。它在企业智能化转型过程中扮演着关键角色&#xff0c;为企业提供了高效、稳定的通信解决方案&#xff0c;助力企业实现智能化转型。 首先&#xff0c;P…

可转债全部历史因子数据,提供api支持

今天在写可转债系统&#xff0c;顺便下载了一下服务器的可转债数据&#xff0c;给大家研究使用 from trader_tool.stock_data import stock_datafrom trader_tool.lude_data_api import lude_data_apiimport osclass convertible_bond_back_test_system: 可转债回测系统…

弱监督语义/实例/全景分割综述2022

摘要 我们从一个统一的角度总结了现有的高效标签图像分割方法&#xff0c;讨论了一个重要的问题:如何弥合弱监督和密集预测之间的差距——目前的方法大多是基于启发式先验&#xff0c;如跨像素相似性、跨标签约束、跨视图一致性和跨图像关系。最后&#xff0c;对标签高效深度图…

黑苹果睡眠总是自动唤醒(RTC)

黑苹果睡眠总是自动唤醒【RTC】 1. 问题2. 解决方案2.1. 查看重启日志2.2. 配置Disable RTC wake scheduling补丁 3. 后续4. 参考 1. 问题 黑苹果EFI 更换后&#xff0c;总是在手动 睡眠后&#xff0c;间歇性重启&#xff0c;然后再次睡眠&#xff0c;然后再重启。原因归结为&…

HX519 防倒流数据线芯片IC

一般概述 苹果iPhone防倒流数据线芯片&#xff0c;可完美支持iPhone、iPad、iPod等8针闪电接口的数据传输同步功能及充电功能。 特点 ❥集成度高&#xff0c;极少的外围元器件。 ❥电路简单&#xff0c;价格优势明显。 ❥稳定性高&#xff0c;兼容性强。 ❥与市面上普通…

meilisearch,老版本的文档

Elasticsearch 做为老牌搜索引擎&#xff0c;功能基本满足&#xff0c;但复杂&#xff0c;重量级&#xff0c;适合大数据量。 MeiliSearch 设计目标针对数据在 500GB 左右的搜索需求&#xff0c;极快&#xff0c;单文件&#xff0c;超轻量。 所以&#xff0c;对于中小型项目来说…

golang函数

【1】函数&#xff1a; 对特定的功能进行提取&#xff0c;形成一个代码片段&#xff0c;这个代码片段就是我们所说的函数 【2】函数的作用&#xff1a;提高代码的复用性 【3】函数和函数是并列的关系&#xff0c;所以我们定义的函数不能写到main函数中 【4】基本语法 func 函…

北交字节联合提出ClassDiffusion: 使用显式类别引导的一致性个性化生成。

在个性化生成领域, 微调可能会引起过拟合导致模型无法生成与提示词一致的结果。针对这个问题&#xff0c;北交&字节联合提出ClassDiffusion&#xff0c;来提升个性化生成的一致性。 通过两个重要观察及理论分析提出了新的观点:一致性的损失是个性化概念语义偏移导致的, 还…

three.js 第四节 - 创建顶点(索引的使用)

顶点不共用&#xff08;不使用索引&#xff09; // 引入three.js import * as THREE from three // 导入轨道控制器 import { OrbitControls } from three/examples/jsm/controls/OrbitControlsconst scence new THREE.Scene()const camera new THREE.PerspectiveCamera(45, …

cesium 多边形加边框宽度 Polygon outlineWidth

cesium中用polygon添加多边形时&#xff0c;设置outlineWidth无效&#xff0c;常见做法是在添加polygon的同时加一个polyline&#xff0c;但是当多边形相邻两条边的角度比较小的情况下&#xff0c;这两个点的连接处有明显的交叉。 解决方案&#xff1a; 第一步&#xff1a;通过…

永磁同步电机滞环电流控制(PI双闭环)matlab仿真模型

微♥“电击小子程高兴的MATLAB小屋”获取模型 1.滞环电流控制的原理 将给定的电流信号与反馈的电流信号进行比较&#xff0c;然后控制它俩之间的差值稳定在一个滞环范围内&#xff0c;若超出范围&#xff0c;则进行相应的调节操作。 操作如下叙述&#xff1a;假设以三相中的A相…

网络安全领域国内外有哪些法律法规?

1. 中国 1.中华人民共和国网络安全法&#xff08;简称网安法&#xff09; 生效时间&#xff1a;2017年6月1日主要内容&#xff1a;规范网络运营行为&#xff0c;维护网络安全&#xff0c;保护国家安全和公共利益&#xff0c;以及保护公民、法人和其他组织的合法权益。 2.中华…

使用AlphaCodium进行代码生成,从提示工程到流程工程

AlphaCodium 的代码生成方法 论文地址&#xff1a;https://arxiv.org/pdf/2401.08500.pdf 源码地址&#xff1a;https://github.com/codium-ai/alphacodium 研究要点包括 **挑战&#xff1a;**现有的自然语言优化方法无法扩展 LLM 的代码生成能力**解决方案&#xff1a;**使…

通过搭建 24 点小游戏应用实战,带你了解 AppBuilder 的技术原理

本文将通过一个 24 点小游戏的案例&#xff0c;详细介绍百度智能云千帆 AppBuilder 的基本技术原理和使用方法&#xff0c;帮助读者快速掌握 AI 原生应用的开发流程。 1 三步构建 AI 原生应用方法论 AI 原生应用与传统应用的最大区别是交互形态彻底的拟人化&#xff0c;通过…

推荐这两款AI工具,真的很好用

巨日禄 巨日禄是一款由杭州巨日禄科技有限公司开发的AI工具&#xff0c;主要功能是将文本内容转换为视频。该工具通过分析大量的剧本数据和影视作品&#xff0c;为用户提供各种类型的故事情节和角色设置&#xff0c;帮助用户快速找到灵感&#xff0c;减少构思剧本的困难和犹豫。…

地级市海拔标准差(可用作宽带中国工具变量)

地级市海拔标准差&#xff08;可用作宽带中国工具变量&#xff09; 1、来源&#xff1a;地理空间数据云 2、指标&#xff1a;行政区划代码、地区、所属省份、所属地域、经度、纬度、海拔标准差&#xff08;m&#xff09; 3、说明&#xff1a;地形起伏度会影响网络基础设施建…