网站首页 > 技术文章 正文
前言
MyBatis内部封装了JDBC,简化了加载驱动、创建连接、创建statement等繁杂的过程,是我们常见的持久性框架。缓存是在计算机内存中保存的临时数据,读取时无需再从磁盘中读取,从而减少数据库的查询次数,提高执行效率。Mybatis提供了一级缓存和二级缓存的支持,默认情况下只开启一级缓存。本次将带着大家深入了解Mybatis的一级缓存机制。
介绍
当我们访问数据库时,Mybatis会创建出一个SqlSession对象开启一次数据库会话。在这次会话中,我们可能会执行多次相同的查询语句,如果不采取措施,每一次查询都会访问数据库,造成资源的浪费。为了优化这部分场景,Mybatis提供了一级缓存的方案,如果是相同的SQL语句,会优先命中一级缓存,避免直接对数据库进行查询。具体执行过程如下图所示。
每个SqlSession中都持有Executor执行器,每个Excutor中有一个LocalCache缓存。Mybatis根据当前执行的SQL生产MappedStatement,在缓存中进行查询,如果缓存命中的话,直接返回结果给用户,如果缓存没有命中,查询数据库,结果写入LocalCache,再返回结果给用户。具体实现类的类关系图如下所示:
源码分析
我们将对MyBatis的源码进行分析,深入理解底层原理。
SqlSession: SqlSession是应用程序与持久层执行交互操作的单线程对象,代表着一个资源的启用,包含所有执行SQL操作的方法。默认实现类是DefaultSqlSession。
主要作用:1、获取Mapper接口;2、发送SQL给数据库;3、控制数据库事务
Executor执行器:Executor定义了查询、更新、事务、缓存操作相关的接口方法,由SqlSession依赖,并受其调度与管理。核心功能是调度执行SQL。在Executor内部完成对LocalCache的查询和写入。
Cache:Mybatis中的Cache接口,提供了缓存的基本操作。
PerpetualCache是对Cache接口最基本的实现,其原理非常简单,内部持有HashMap,对一级缓存的操作实则是对HashMap的操作。如下代码所示:
客户端与数据库的交互,首先通过DefaultSqlSessionFactory开启SqlSession:
在初始化SqlSession时,会通过configuration类创建一个新的Executor:
SqlSession创建完毕后,根据不同类型的Statement,SqlSession会执行对应的方法。比如Select语句,最终会执行到selectList方法
SqlSession把具体的查询交给Executor去执行。如果只开启了一级缓存的话,首先会进入BaseExecutor的query方法。代码如下所示:
会根据传入的参数生成CacheKey作为唯一标识,进入方法,查看具体的生成逻辑:
在上述的代码中,将MappedStatement的Id、SQL的offset、SQL的limit、SQL本身以及SQL中的参数传入了CacheKey这个类,最终构成CacheKey。在CacheKey的update方法中,会进行一个hashcode和checksum的计算,同时把传入的参数添加进updatelist中。如下代码所示:
只要两条SQL的下列五个值相同,即可以认为是相同的SQL。
Statement Id + Offset + Limmit + Sql + Params
如果在LocalCache查不到的话,就会查询数据库,并将结果写入至缓存。
如果是insert/update/delete方法,统一都会走SqlSession的update流程,同样委托执行器执行SQL。
执行方法如下,我们可以发现每次执行update前都会清空LocalCache。所以当发生修改操作时,一级缓存会失效,防止了数据脏读。
总结
1、一级缓存LocalCache只是BaseExecutor的一个字段。所以,当SqlSession的生命周期结束时,一级缓存也会被回收。
2、一级缓存由PerpetualCache实现,其内部通过HashMap完成对缓存的读写操作,所以本质上讲,一级缓存其实是通过HashMap实现的,Map中的key是由MappedStatement生成CacheKey,来确保查询的唯一性。
3、MyBatis的一级缓存最大范围是SqlSession内部,有多个SqlSession或者分布式的环境下,数据库写操作会引起脏数据,建议设定缓存级别为Statement。
猜你喜欢
- 2024-10-26 MybatisPlus —注解汇总(mybatis中注解)
- 2024-10-26 MyBatis使用需谨慎,看看这里有没有你曾踩到过的坑
- 2024-10-26 最新版本的MyBatis Plus 代码生成器使用指南
- 2024-10-26 解决mybatis动态生成sql错误的问题
- 2024-10-26 基于 MyBatis 的动态 SQL 技术详解
- 2024-10-26 mybatis插入获取主键的方式和原理
- 2024-10-26 Mybatis执行多条语句/批量更新方式
- 2024-10-26 MyBatis-Plus扫盲啦(mybatis plus vo)
- 2024-10-26 一文带你搞定mybatis的映射配置文件
- 2024-10-26 什么是mybatis-plus,你没用过吧,我刚学的,...
- 1507℃桌面软件开发新体验!用 Blazor Hybrid 打造简洁高效的视频处理工具
- 505℃Dify工具使用全场景:dify-sandbox沙盒的原理(源码篇·第2期)
- 484℃MySQL service启动脚本浅析(r12笔记第59天)
- 465℃服务器异常重启,导致mysql启动失败,问题解决过程记录
- 462℃启用MySQL查询缓存(mysql8.0查询缓存)
- 442℃「赵强老师」MySQL的闪回(赵强iso是哪个大学毕业的)
- 422℃mysql服务怎么启动和关闭?(mysql服务怎么启动和关闭)
- 418℃MySQL server PID file could not be found!失败
- 最近发表
-
- netty系列之:搭建HTTP上传文件服务器
- 让deepseek教我将deepseek接入word
- 前端大文件分片上传断点续传(前端大文件分片上传断点续传怎么操作)
- POST 为什么会发送两次请求?(post+为什么会发送两次请求?怎么回答)
- Jmeter之HTTP请求与响应(jmeter运行http请求没反应)
- WAF-Bypass之SQL注入绕过思路总结
- 用户疯狂点击上传按钮,如何确保只有一个上传任务在执行?
- 二 计算机网络 前端学习 物理层 链路层 网络层 传输层 应用层 HTTP
- HTTP请求的完全过程(http请求的基本过程)
- dart系列之:浏览器中的舞者,用dart发送HTTP请求
- 标签列表
-
- c++中::是什么意思 (83)
- 标签用于 (65)
- 主键只能有一个吗 (66)
- c#console.writeline不显示 (75)
- pythoncase语句 (81)
- es6includes (73)
- windowsscripthost (67)
- apt-getinstall-y (86)
- node_modules怎么生成 (76)
- chromepost (65)
- c++int转char (75)
- static函数和普通函数 (76)
- el-date-picker开始日期早于结束日期 (70)
- js判断是否是json字符串 (67)
- checkout-b (67)
- localstorage.removeitem (74)
- vector线程安全吗 (70)
- & (66)
- java (73)
- js数组插入 (83)
- linux删除一个文件夹 (65)
- mac安装java (72)
- eacces (67)
- 查看mysql是否启动 (70)
- 无效的列索引 (74)