mybatis源码 (四) —— 一级缓存和二级缓存

2024-06-15 21:08

本文主要是介绍mybatis源码 (四) —— 一级缓存和二级缓存,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

MyBatis的缓存分为两种

  1. 一级缓存,一级缓存是SqlSession级别的缓存,对于相同的查询,会从缓存中返回结果而不是查询数据库(也叫本地缓存)默认会启用
  2. 二级缓存,二级缓存是Mapper级别的缓存,定义在Mapper文件的标签中并需要开启此缓存,默认关闭

先看二级缓存:开启
org.apache.ibatis.executor.CachingExecutor#query

  //被ResultLoader.selectList调用@Overridepublic <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)throws SQLException {Cache cache = ms.getCache();//默认情况下是没有开启缓存的(二级缓存).要开启二级缓存,你需要在你的 SQL 映射文件中添加一行: <cache/>//简单的说,就是先查CacheKey,查不到再委托给实际的执行器去查if (cache != null) {flushCacheIfRequired(ms);if (ms.isUseCache() && resultHandler == null) {ensureNoOutParams(ms, parameterObject, boundSql);@SuppressWarnings("unchecked")List<E> list = (List<E>) tcm.getObject(cache, key);if (list == null) {list = delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);tcm.putObject(cache, key, list); // issue #578 and #116}return list;}}return delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);}
  1. 根据flushCache=true或者flushCache=false判断是否要清理二级缓存
  2. 从tcm中获取查询结果,这个tcm解释一下,
  3. 如果没有从MyBatis二级缓存中拿到数据,那么就会查一次数据库,然后放到MyBatis二级缓存中去

总结:二级缓存是存在MappedStatement 即mapper中


再看一级缓存:
org.apache.ibatis.executor.BaseExecutor#query

  @Overridepublic <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());//如果已经关闭,报错if (closed) {throw new ExecutorException("Executor was closed.");}//先清局部缓存,再查询.但仅查询堆栈为0,才清。为了处理递归调用if (queryStack == 0 && ms.isFlushCacheRequired()) {clearLocalCache();}List<E> list;try {//加一,这样递归调用到上面的时候就不会再清局部缓存了queryStack++;//先根据cachekey从localCache去查list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;if (list != null) {//若查到localCache缓存,处理localOutputParameterCachehandleLocallyCachedOutputParameters(ms, key, parameter, boundSql);} else {//从数据库查list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);}
  1. 先根据cachekey从localCache去查
  2. 若查到localCache缓存,处理localOutputParameterCache
  3. 没查到再从数据库查
  //从数据库查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);//如果是存储过程,OUT参数也加入缓存if (ms.getStatementType() == StatementType.CALLABLE) {localOutputParameterCache.putObject(key, parameter);}return list;}

从数据库查到数据之后保存在一级缓存里面
总结:一级缓存是在BaseExecutor中维护的,也就是说一级缓存对应的SQLSession


而对于org.apache.ibatis.executor.CachingExecutor#update

  @Overridepublic int update(MappedStatement ms, Object parameterObject) throws SQLException {//刷新缓存完再updateflushCacheIfRequired(ms);return delegate.update(ms, parameterObject);}

二级缓存的更新操作,先清除缓存,在更新

org.apache.ibatis.executor.BaseExecutor#update

  @Overridepublic int update(MappedStatement ms, Object parameter) throws SQLException {ErrorContext.instance().resource(ms.getResource()).activity("executing an update").object(ms.getId());if (closed) {throw new ExecutorException("Executor was closed.");}//先清局部缓存,再更新,如何更新交由子类,模板方法模式clearLocalCache();return doUpdate(ms, parameter);}

一级缓存的更新操作,先清除缓存,在更新

这篇关于mybatis源码 (四) —— 一级缓存和二级缓存的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1064576

相关文章

MySQL 缓存机制与架构解析(最新推荐)

《MySQL缓存机制与架构解析(最新推荐)》本文详细介绍了MySQL的缓存机制和整体架构,包括一级缓存(InnoDBBufferPool)和二级缓存(QueryCache),文章还探讨了SQL... 目录一、mysql缓存机制概述二、MySQL整体架构三、SQL查询执行全流程四、MySQL 8.0为何移除查

Go中sync.Once源码的深度讲解

《Go中sync.Once源码的深度讲解》sync.Once是Go语言标准库中的一个同步原语,用于确保某个操作只执行一次,本文将从源码出发为大家详细介绍一下sync.Once的具体使用,x希望对大家有... 目录概念简单示例源码解读总结概念sync.Once是Go语言标准库中的一个同步原语,用于确保某个操

在MyBatis的XML映射文件中<trim>元素所有场景下的完整使用示例代码

《在MyBatis的XML映射文件中<trim>元素所有场景下的完整使用示例代码》在MyBatis的XML映射文件中,trim元素用于动态添加SQL语句的一部分,处理前缀、后缀及多余的逗号或连接符,示... 在MyBATis的XML映射文件中,<trim>元素用于动态地添加SQL语句的一部分,例如SET或W

Mybatis官方生成器的使用方式

《Mybatis官方生成器的使用方式》本文详细介绍了MyBatisGenerator(MBG)的使用方法,通过实际代码示例展示了如何配置Maven插件来自动化生成MyBatis项目所需的实体类、Map... 目录1. MyBATis Generator 简介2. MyBatis Generator 的功能3

Redis缓存问题与缓存更新机制详解

《Redis缓存问题与缓存更新机制详解》本文主要介绍了缓存问题及其解决方案,包括缓存穿透、缓存击穿、缓存雪崩等问题的成因以及相应的预防和解决方法,同时,还详细探讨了缓存更新机制,包括不同情况下的缓存更... 目录一、缓存问题1.1 缓存穿透1.1.1 问题来源1.1.2 解决方案1.2 缓存击穿1.2.1

Mybatis提示Tag name expected的问题及解决

《Mybatis提示Tagnameexpected的问题及解决》MyBatis是一个开源的Java持久层框架,用于将Java对象与数据库表进行映射,它提供了一种简单、灵活的方式来访问数据库,同时也... 目录概念说明MyBATis特点发现问题解决问题第一种方式第二种方式问题总结概念说明MyBatis(原名

SpringBoot基于MyBatis-Plus实现Lambda Query查询的示例代码

《SpringBoot基于MyBatis-Plus实现LambdaQuery查询的示例代码》MyBatis-Plus是MyBatis的增强工具,简化了数据库操作,并提高了开发效率,它提供了多种查询方... 目录引言基础环境配置依赖配置(Maven)application.yml 配置表结构设计demo_st

Redis与缓存解读

《Redis与缓存解读》文章介绍了Redis作为缓存层的优势和缺点,并分析了六种缓存更新策略,包括超时剔除、先删缓存再更新数据库、旁路缓存、先更新数据库再删缓存、先更新数据库再更新缓存、读写穿透和异步... 目录缓存缓存优缺点缓存更新策略超时剔除先删缓存再更新数据库旁路缓存(先更新数据库,再删缓存)先更新数

el-select下拉选择缓存的实现

《el-select下拉选择缓存的实现》本文主要介绍了在使用el-select实现下拉选择缓存时遇到的问题及解决方案,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的... 目录项目场景:问题描述解决方案:项目场景:从左侧列表中选取字段填入右侧下拉多选框,用户可以对右侧

解决mybatis-plus-boot-starter与mybatis-spring-boot-starter的错误问题

《解决mybatis-plus-boot-starter与mybatis-spring-boot-starter的错误问题》本文主要讲述了在使用MyBatis和MyBatis-Plus时遇到的绑定异常... 目录myBATis-plus-boot-starpythonter与mybatis-spring-b