关键词:
引言
本篇源码解析基于mybatis 3.5.8版本。
MyBatis 中的缓存指的是 MyBatis 在执行一次SQL查询时,在满足一定的条件下,会把这个sql和对应的查询结果缓存起来。当再次执行相同SQL语句的时候,就会直接从缓存中进行提取,而不是请求到数据库。当然如果中间有更新操作,缓存会失效。
MyBatis中的缓存分为一级缓存和二级缓存,一级缓存又被称为 SqlSession 级别的缓存,二级缓存又被称为表级缓存。通俗的说,一级缓存是本次会话有效,二级缓存可以跨越多个会话共享缓存。
当开启缓存后,数据的查询执行的流程就是 二级缓存 -> 一级缓存 -> 数据库。
一级缓存开启的情况下,查询的时序图如下:
二级缓存也是类似的机制。
正文
根据上面的调用时序图我们可以看到mybatis是通过SqlSession
统一对外的,SqlSession是接口,有个默认实现类:DefaultSqlSession
。来看下这个类。
public class DefaultSqlSession implements SqlSession
private final Configuration configuration;
//每个SqlSession中持有了Executor
private final Executor executor;
private final boolean autoCommit;
private boolean dirty;
private List<Cursor<?>> cursorList;
...
DefaultSqlSession
持有Executor
的引用,事实上对sqlSession的操作都是委托给这个Executor进行的,比如select方法,最终会调用selectList方法:
private <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler)
try
MappedStatement ms = configuration.getMappedStatement(statement);
return executor.query(ms, wrapCollection(parameter), rowBounds, handler);
catch (Exception e)
throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e);
finally
ErrorContext.instance().reset();
Executor
是个接口,有个默认的抽象类实现BaseExecutor
,这个抽象类持有一个名为``PerpetualCache`的引用,这是个本地缓存的实现类,缓存就是通过这个类来管理的。
public abstract class BaseExecutor implements Executor
...
//本地缓存实现类
protected PerpetualCache localCache;
protected PerpetualCache localOutputParameterCache;
protected Configuration configuration;
...
到这里,我们总结下提到的这几个类的关系:
前面提到BaseExecutor
是个抽象类,定义若干抽象方法,方法如下:
protected abstract int doUpdate(MappedStatement ms, Object parameter) throws SQLException;
protected abstract List<BatchResult> doFlushStatements(boolean isRollback) throws SQLException;
protected abstract <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql)
throws SQLException;
protected abstract <E> Cursor<E> doQueryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds, BoundSql boundSql)
throws SQLException;
在执行的时候,把具体的操作委托给对应的实现类进行执行。有几个实现类,为Executor赋予了不同的能力,如下图所示:
BatchExecutor专门用于执行批量sql操作。而SimpleExecutor用于简单sql场景。至于选择哪个,是配置决定的:
<setting name="defaultExecutorType" value="SIMPLE"/>
既然DefaultSqlSession
持有Executor
的引用,它是什么时候初始化的呢?答案是在org.apache.ibatis.session.defaults.DefaultSqlSessionFactory#openSession
调用的时候,如下:
private SqlSession openSessionFromConnection(ExecutorType execType, Connection connection)
try
....
final Executor executor = configuration.newExecutor(tx, execType);
return new DefaultSqlSession(configuration, executor, autoCommit);
catch (Exception e)
throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e);
finally
ErrorContext.instance().reset();
继续跟下去,org.apache.ibatis.session.Configuration#newExecutor
public Executor newExecutor(Transaction transaction, ExecutorType executorType)
//默认的executor类型是SIMPLE
executorType = executorType == null ? defaultExecutorType : executorType;
executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
Executor executor;
if (ExecutorType.BATCH == executorType)
executor = new BatchExecutor(this, transaction);
else if (ExecutorType.REUSE == executorType)
executor = new ReuseExecutor(this, transaction);
else
executor = new SimpleExecutor(this, transaction);
//如果二级缓存开关开启的话,是使用CahingExecutor装饰BaseExecutor的子类
if (cacheEnabled)
executor = new CachingExecutor(executor);
/**
* 插件化处理
*/
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
大部分时候,我们使用的是SimpleExecutor
,注意二级缓存如果开关打开使用的是CachingExecutor
,我们下篇文章再来分析二级缓存。 插件化处理那里,后面也会有专门的文章进行解析。
来继续看看PerpetualCache
,BaseExecutor持有它的引用对缓存进行管理。查询的逻辑如下:
@Override
public <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.");
if (queryStack == 0 && ms.isFlushCacheRequired())
clearLocalCache();
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--;
...
代码逻辑比较清晰,先查缓冲,没有命中就查数据库。PerpetualCache其实是持有一个map进行本地缓存。然后查询db后再更新cache:
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;
public class PerpetualCache implements Cache
private final String id;
private final Map<Object, Object> cache = new HashMap<>();
...
Cache
接口有几个实现类,结构如下:
这些不同的实现类彼此通过装饰器模式互相装饰,实现功能的互补。比如说,
public class LruCache implements Cache
private final Cache delegate;
private Map<Object, Object> keyMap;
private Object eldestKey;
...
初始化的时候可以传入一个其它cache实现类的,复用这个类的功能。
LruCache cache = new LruCache(new PerpetualCache("default"));
cache.setSize(5);
for (int i = 0; i < 5; i++)
cache.putObject(i, i);
...
具体的每个实现类不是本文的重点,这里不表。
一级缓存就写到这里吧。
参考
- https://tech.meituan.com/2018/01/19/mybatis-cache.html
mybatis源码分析-缓存原理(代码片段)
...ache3.CacheKey4.一级缓存5.二级缓存6.总结参考序号内容链接1MyBatis源码分析-MyBatis入门https://thinkwon.blog.csdn.net/article/details/1148088522MyBatis源码分析-配置文件解析过程https://thinkwon.blog.csdn.net/article/details/1148089623MyBatis源码分析-映射文件... 查看详情
mybatis缓存专题-一文彻底搞懂mybatis一级缓存(代码片段)
...不能使用2.什么是一级缓存3.什么情况下会命中一级缓存4.Mybatis的一级缓存机制详解5.MyBatis关闭一级缓存6.Mybatis的一级缓存机制源码分析7.Mybatis的一级缓存机制源码分析图解总结8.一级缓存什么时候被清空?9.一级缓存key是什... 查看详情
mybatis缓存专题-一文彻底搞懂mybatis一级缓存(代码片段)
...不能使用2.什么是一级缓存3.什么情况下会命中一级缓存4.Mybatis的一级缓存机制详解5.MyBatis关闭一级缓存6.Mybatis的一级缓存机制源码分析7.Mybatis的一级缓存机制源码分析图解总结8.一级缓存什么时候被清空?9.一级缓存key是什... 查看详情
mybatis源码分析之06二级缓存
上一篇整合redis框架作为mybatis的二级缓存, 该篇从源码角度去分析mybatis是如何做到的。通过上一篇文章知道,整合redis时需要在FemaleMapper.xml中添加如下配置<cacheeviction="LRU"type="qinfeng.zheng.RedisCache"/> MYBATIS源码分析之02... 查看详情
mybatis源码分析五mybatis的缓存
五、MyBatis缓存文章目录五、MyBatis缓存缓存的概念与应用缓存的概念开发一个简单的缓存MyBatis中的缓存设计自定义一个Cache实现类MyBatis中的Cache实现类PerpetualCache装饰器CacheCache如何在MyBatis运行过程应用MyBatis缓存的二层体系一级... 查看详情
mybatis源码分析五mybatis的缓存
五、MyBatis缓存文章目录五、MyBatis缓存缓存的概念与应用缓存的概念开发一个简单的缓存MyBatis中的缓存设计自定义一个Cache实现类MyBatis中的Cache实现类PerpetualCache装饰器CacheCache如何在MyBatis运行过程应用MyBatis缓存的二层体系一级... 查看详情
mybatis源码分析-插件机制(代码片段)
...3.2执行插件逻辑4.实现一个分页插件5.总结序号内容链接1MyBatis源码分析-MyBatis入门https://thinkwon.blog.csdn.net/article/details/1148088522MyBatis源码分析-配置文件解析过程https://thinkwon.blog.csdn.net/article/details/1148089623MyBatis源码分析-映射文件解... 查看详情
mybatis从入门到精通—源码剖析之二级缓存细节(代码片段)
⼆级缓存构建在⼀级缓存之上,在收到查询请求时,MyBatis⾸先会查询⼆级缓存,若⼆级缓存未命中,再去查询⼀级缓存,⼀级缓存没有,再查询数据库。⼆级缓存------》⼀级缓存------》数据库与⼀级缓存不同,⼆级缓存和具体... 查看详情
源码级mybatis缓存策略(一级和二级缓存)(代码片段)
...们可以避免频繁的与数据库进行交互,进而提高响应速度MyBatis也提供了对缓存的支持,分为一级缓存和二级缓存,可以通过下图来理解:①、一级缓存是SqlSession级别的缓存。在操作数据库时需要构造sqlSession对象,在对象中有一... 查看详情
android:深入剖析图片加载库glide缓存功能(源码分析)
参考技术AGlide需要缓存的图片资源分为两类:Glide的缓存机制使得Glide具备非常好的图片缓存效果,从而使得具备较高的图片加载效率。下面,我将根据Glide缓存流程中的每个步骤进行源码分析。至此,Glide的图片缓存Key生成完毕... 查看详情
mybatis缓存的使用和源码分析
Mybatis缓存使用在Mybatis中缓存分为一级缓存和二级缓存,二级缓存又称为全局缓存,默认一级缓存和二级缓存都是开启的,只是二级缓存的使用需要配置才能生效,在Mybatis中一级缓存是SqlSession级别也就是会话级别的,而二级缓... 查看详情
mybatis-缓存机制
MyBatis包含一个非常强大的查询缓存特性,它可以非常方便地配置和定制。缓存可以极大的提升查询效率。MyBatis系统中默认定义了两级缓存,一级缓存和二级缓存。–1、默认情况下,只有一级缓存(SqlSession级别的缓存,也称为本... 查看详情
mybatis-缓存机制
MyBatis包含一个非常强大的查询缓存特性,它可以非常方便地配置和定制。缓存可以极大的提升查询效率。 MyBatis系统中默认定义了两级缓存。 一级缓存和二级缓存。 一级缓存:(本地缓存):SqlSession级别的缓存,一级缓存是一致开... 查看详情
ssm框架mybatis笔记---表之间的关联关系;mybatis事务;mybatis缓存机制;orm概述
MyBatis框架提供两级缓存,一级缓存和二级缓存,默认开启一级缓存。缓存就是为了提交查询效率MyBatis框架提供两级缓存,一级缓存和二级缓存,默认开启一级缓存。缓存就是为了提交查询效率 查看详情
mybatis之缓存机制
1、缓存机制的简单介绍: a、MyBatis包含一个非常强大的查询缓存特性,它可以非常方便地配置和定制。缓存可以极大的提升查询效率。 b、MyBatis系统中默认定义了两级缓存:&... 查看详情
通过源码分析mybatis的缓存
看了通过源码分析MyBatis的缓存这篇文章后,自己跟着源码过了一遍,对mybatis的一级缓存和二级缓存有了更清楚的认识。 一级缓存是SqlSession级别的,同一个sqlSession在第二次执行一个相同参数的select语句并且第一次执行... 查看详情
mybatis:缓存机制
...缓存,减少与数据库交互次数,减少系统开销,提高效率MyBatis系统中默认定义了两级缓存:一级缓存和二级缓存一级缓存开启。(SqlSession级别的缓存,也称为本地缓存)二级缓存需要手动开启和配置,他 查看详情
《深入理解mybatis原理4》mybatis缓存机制的设计与实现
《深入理解mybatis原理》MyBatis缓存机制的设计与实现本文主要讲解MyBatis非常棒的缓存机制的设计原理,给读者们介绍一下MyBatis的缓存机制的轮廓,然后会分别针对缓存机制中的方方面面展开讨论。MyBatis将数据缓存设计成两级结... 查看详情