详解Mybatis的缓存
作者:Narule 发布时间:2022-10-23 22:49:13
Mybatis的缓存
mybatis是一个查询数据库的封装框架,主要是封装提供灵活的增删改sql,开发中,service层能够通过mybatis组件查询和修改数据库中表的数据;作为查询工具,mybatis有使用缓存,这里讲一下mybatis的缓存相关源码。
缓存
在计算机里面,任何信息都有源头,缓存一般指源头信息读取后,放在内存或者其他读取较快的地方,下次读取相同信息不去源头查询而是直接从内存(或者能快速存取的硬件)读取。这样可以减少硬件使用,提高读取速度。
mybatis也是这样,查询数据库的数据之后,mybatis可以把查询结果缓存到内存,下次查询如果查询语句相同,并且查询相关的表的数据没被修改过,就可以直接返回缓存中的结果,而不用去查询数据库的语句,有效节省了时间。
简单看一下mybatis一级缓存和二级缓存相关源码,学习使用
一级缓存
通过查看源码可知,一级缓存是绑定sqSsession中的,所以每次查询sqlSession不同就失效,相同的sqlSession可以使用一级缓存。
mybatis默认sqlsession:org.apache.ibatis.session.defaults.DefaultSqlSession
构造方法中传入executor(查询执行对象)
public DefaultSqlSession(Configuration configuration, Executor executor, boolean autoCommit) {
this.configuration = configuration;
this.executor = executor;
this.dirty = false;
this.autoCommit = autoCommit;
}
executor中携带一级缓存成员:
protected BaseExecutor(Configuration configuration, Transaction transaction) {
this.transaction = transaction;
this.deferredLoads = new ConcurrentLinkedQueue<>();
this.localCache = new PerpetualCache("LocalCache"); //默认一级缓存
this.localOutputParameterCache = new PerpetualCache("LocalOutputParameterCache");
this.closed = false;
this.configuration = configuration;
this.wrapper = this;
}
查询使用一级缓存逻辑
org.apache.ibatis.executor.BaseExecutor.query()
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());
List<E> list;
try {
queryStack++;
//localCache 一级缓存
list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
//先从一级缓存中获取,key是通过sql语句生成
if (list != null) {
handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
} else {
// 如果缓存中没有 才从数据库查询
list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
} finally {
queryStack--;
}
return list;
}
//从数据库读取数据
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;
}
二级缓存
二级缓存mapper中的,默认是开启的,但需要在映射文件mapper.xml中添加<cache/>标签
<mapper namespace="userMapper">
<cache/><!-- 添加cache标签表示此mapper使用二级缓存 -->
</mapper>
配置false可以关闭二级缓存
二级缓存的解析
org.apache.ibatis.builder.xml.XMLMapperBuilder
private void configurationElement(XNode context) {
try {
//...
cacheElement(context.evalNode("cache")); //解析cache标签
} catch (Exception e) {
throw new BuilderException("Error parsing Mapper XML. The XML location is '" + resource + "'. Cause: " + e, e);
}
}
private void cacheElement(XNode context) {
if (context != null) { // if hava cache tag 如果有cache标签才执行下面的逻辑
String type = context.getStringAttribute("type", "PERPETUAL");
Class<? extends Cache> typeClass = typeAliasRegistry.resolveAlias(type);
String eviction = context.getStringAttribute("eviction", "LRU");
Class<? extends Cache> evictionClass = typeAliasRegistry.resolveAlias(eviction);
Long flushInterval = context.getLongAttribute("flushInterval");
Integer size = context.getIntAttribute("size");
boolean readWrite = !context.getBooleanAttribute("readOnly", false);
boolean blocking = context.getBooleanAttribute("blocking", false);
Properties props = context.getChildrenAsProperties();
builderAssistant.useNewCache(typeClass, evictionClass, flushInterval, size, readWrite, blocking, props);//建立二级缓存
}
}
org.apache.ibatis.builder.MapperBuilderAssistant.useNewCache():
public Cache useNewCache(Class<? extends Cache> typeClass,
Class<? extends Cache> evictionClass,
Long flushInterval,
Integer size,
boolean readWrite,
boolean blocking,
Properties props) {
Cache cache = new CacheBuilder(currentNamespace)
.implementation(valueOrDefault(typeClass, PerpetualCache.class))
.addDecorator(valueOrDefault(evictionClass, LruCache.class))
.clearInterval(flushInterval)
.size(size)
.readWrite(readWrite)
.blocking(blocking)
.properties(props)
.build();
configuration.addCache(cache);//二级缓存赋值,如果cache标签为空,不会执行此方法,currentCache为空
currentCache = cache;
return cache;
}
在映射文件mapper中如果没有cache标签,不会执行上面的useNewCache方法,cache为null,就不会使用二级缓存(相当于失效)。
查询使用二级缓存逻辑
org.apache.ibatis.executor.CachingExecutor :
@Override
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) {//如果二级缓存对象不为空 尝试在二级缓存中获取(没有cache标签此对象就是空)
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); //如果为空,使用delegate查询(BaseExecutor)
tcm.putObject(cache, key, list); // 查询结果保存到二级缓存
}
return list;
}
}
return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
二级缓存和一级缓存不用想,数据库的数据被修改是要清空缓存的,不然数据有误,至于怎么清空,是另一套逻辑了,mapper中的cache标签可以配置一些参数,比如缓存定期清空。
一级二级缓存先后顺序
mybatis默认是先查询二级缓存,没有,再查看一级缓存,都为空,最后查询数据库
来源:https://www.cnblogs.com/Narule/p/14284868.html
猜你喜欢
- Spring相关的依赖导入进去,即可使用spring的定时任务!<!-- spring核心包 -->
- 图片解析:1.生成字节码文件的过程可能产生编译时异常(checked),由字节码文件到在内存中加载、运行类此过程可能产生运行时异常(unch
- 本文实例讲述了Java实现的读取资源文件工具类ResourcesUtil。分享给大家供大家参考,具体如下:package com.gclou
- 概述从今天开始, 小白我将带大家开启 Java 数据结构 & 算法的新篇章.链表链表 (Linked List) 是一种递归的动态数
- 目录为什么要用Geometry数据做图标?怎么获取Geometry数据?如何使用Geometry数据相信大家在阅读WPF相关GitHub开源
- 一 前言学习微服务要从基础的架构学起,首先你要有个微服务的概念才能学习对吧!!如果你都不知道啥是微服务,就一头扎进去学习,你自己也觉得自己也
- Java xml出现错误 javax.xml.transform.TransformerException: java.lang.NullP
- 本文主要介绍我为桌面和 Web 设计的一个超级秘密 Flutter 项目使用了画布和可拖动节点界面。本教程将展示我如何使用堆栈来使用小部件完
- 一、介绍Spring是通过任务执行器(TaskExecutor)来实现多线程和并发编程,使用Spring提供的ThreadPoolTaskE
- 数据结构是数据存储的方式,算法是数据计算的方式。所以在开发中,算法和数据结构息息相关。今天的讲义中会涉及部分数据结构的专业名词,如果各位铁粉
- 带你手把手,用 java swing实现抖音上的表白程序1.准备工作a.需要下载一个带着swing插件的eclipseb.需要配置好JDKc
- Executor接口基于以下方法可以完成增,删,改查以及事务处理等操作。事实上,mybatis中的所有数据库操作是通过调用这些方法实现的。p
- 本文实例为大家分享了java实现PDF转图片的具体代码,供大家参考,具体内容如下1.首先利用maven引入所需jar包<depende
- FileStream对象表示在磁盘或网络路径上指向文件的流。这个类提供了在文件中读写字节的方法,但经常使用StreamReader或Stre
- springboot 无法自动装配@Autowired 报错:无法自动装配基本上是因为1、项目里有类似mybatis @Mapper这种第三
- maven没有打包xml文件的问题最近使用maven带管理项目,采用SSM的技术栈,在配置好一些配置文件,打包部署到tomcat上,出现没有
- 在上一篇文章《驱动开发:内核字符串转换方法》中简单介绍了内核是如何使用字符串以及字符串之间的转换方法,本章将继续探索字符串的拷贝与比较,与应
- 从Java 5开始,Java语言对方法参数支持一种新写法,叫 可变长度参数列表,其语法就是类型后跟...,表示此处接受的参数为0到多个Obj
- 虽然项目中都夹杂了Hibernate的支持,但是团队开发中,很多人为了编写特殊查询的代码时都使用了JDBC进行查询。JDBC查询后返回的是一
- 前言convert 叫强制转换,可以是其他类型。最近在工作中遇到一个问题,需要将字符串形式的数值转换回数值,很正常的要求吧。却遇到了问题,下