Mybatis源码分析之(七)Mybatis一级缓存和二级缓存的实现

文章目录

    • 一级缓存
    • 二级缓存
    • 总结

对于一名程序员,缓存真的很重要,而且缓存真的是老生常谈的一个话题拉。因为它在我们的开发过程中真的是无处不在。今天LZ带大家来看一下。Mybatis是怎么实现一级缓存和二级缓存的。(自带的缓存机制)

一级缓存

存在BaseExecutor中,是全局的缓存,每次查询后将值存入BaseExecutor的localCache中。key是由ms,parameter,rowBounds和boundSql一起生成的一个值。value就是查询出来的结果。一旦有任何更新变动,就删除整个localCache。

  @Override//生成一级缓存的key的函数,有兴趣的看看,LZ不详细解释了,不是重点。public CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) {if (closed) {throw new ExecutorException("Executor was closed.");}CacheKey cacheKey = new CacheKey();cacheKey.update(ms.getId());cacheKey.update(rowBounds.getOffset());cacheKey.update(rowBounds.getLimit());cacheKey.update(boundSql.getSql());List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();TypeHandlerRegistry typeHandlerRegistry = ms.getConfiguration().getTypeHandlerRegistry();// mimic DefaultParameterHandler logicfor (ParameterMapping parameterMapping : parameterMappings) {if (parameterMapping.getMode() != ParameterMode.OUT) {Object value;String propertyName = parameterMapping.getProperty();if (boundSql.hasAdditionalParameter(propertyName)) {value = boundSql.getAdditionalParameter(propertyName);} else if (parameterObject == null) {value = null;} else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {value = parameterObject;} else {MetaObject metaObject = configuration.newMetaObject(parameterObject);value = metaObject.getValue(propertyName);}cacheKey.update(value);}}if (configuration.getEnvironment() != null) {// issue #176cacheKey.update(configuration.getEnvironment().getId());}return cacheKey;}
//查询数据库并存入一级缓存的语句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) {//如果是存储过程把参数存入localOutputParameterCachelocalOutputParameterCache.putObject(key, parameter);}return list;}@Override//更新变动等语句删除缓存public 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);}@Override//删除一级缓存public void clearLocalCache() {if (!closed) {localCache.clear();localOutputParameterCache.clear();}}

所以一级缓存的作用级别是SESSION级别的,因为一个session中存放一个Executor。而一级缓存放在Executor。

二级缓存

如果开启了二级缓存的话,你的Executor将会被装饰成CachingExecutor,二级缓存是MapperStatement级的缓存,也就是一个namespace就会有一个缓存,缓存是通过CachingExecutor来操作的。查询出来的结果会存在statement中的cache中,若有更新,删除类的操作默认就会清空该MapperStatement的cache(也可以通过修改xml中的属性,让它不执行),不会影响其他的MapperStatement

  @Overridepublic <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)throws SQLException {//获得该MappedStatement的cacheCache cache = ms.getCache();if (cache != null) {//看是否需要清除cache(在xml中可以配置flushCache属性决定何时清空cache)flushCacheIfRequired(ms);if (ms.isUseCache() && resultHandler == null) {//若开启了cache且resultHandler 为空ensureNoOutParams(ms, parameterObject, boundSql);@SuppressWarnings("unchecked")//从TransactionalCacheManager中取cacheList<E> list = (List<E>) tcm.getObject(cache, key);//若没值if (list == null) {//访问数据库list = delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);//存入cachetcm.putObject(cache, key, list); // issue #578 and #116}return list;}}return delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);}@Overridepublic int update(MappedStatement ms, Object parameterObject) throws SQLException {//看是否需要清除cache(在xml中可以配置flushCache属性决定何时清空cache)flushCacheIfRequired(ms);return delegate.update(ms, parameterObject);}private void flushCacheIfRequired(MappedStatement ms) {//获得cacheCache cache = ms.getCache();if (cache != null && ms.isFlushCacheRequired()) {      //若需要清除,则清除cachetcm.clear(cache);}}

因同一个namespace下的MappedStatement的cache是同一个,而TransactionalCacheManager中统一管理cache是里面的属性transactionalCaches,该属性以MappedStatement中的Cache为key,TransactionalCache对象为Value。即一个namespace对应一个TransactionalCache。

public class TransactionalCacheManager {private Map<Cache, TransactionalCache> transactionalCaches = new HashMap<Cache, TransactionalCache>();……
}
public class TransactionalCache implements Cache {private static final Log log = LogFactory.getLog(TransactionalCache.class);//就是对应的namespace中的cacheprivate Cache delegate;//提交的时候清除cache的标志位private boolean clearOnCommit;//待提交的集合private Map<Object, Object> entriesToAddOnCommit;//未查到的key存放的集合private Set<Object> entriesMissedInCache;
}

总结

一级缓存是sqlSession级别的缓存,存放在BaseExecutor中的localCache中。查询就将结果缓存进去,一旦有更新,删除,插入类的操作就清空缓存。不同的sqlSession之间的缓存是互相不影响的。
二级缓存是namespace级别的,可以理解为一个mapper.xml文件对应一个二级缓存(不同的sqlSession之间的缓存是共享的),然后对缓存的操作是在.xml文件中的标签文件进行控制的。比如下面的代码

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="mapper.TDemoMapper">
<!-- 一个namespace对应一个缓存 --><resultMap id="baseMap" type="entity.TDemo"><result property="id" column="id" jdbcType="INTEGER"></result><result property="name" column="name" jdbcType="VARCHAR"></result></resultMap><!-- 执行此语句不清空缓存 --><select id="getAll" resultType="entity.TDemo" useCache="true" flushCache="false" >select * from t_demo</select></mapper>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/555892.shtml

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

相关文章

oauth2 java 获取token_OAuth2 Token 一定要放在请求头中吗?

Token 一定要放在请求头中吗&#xff1f; 答案肯定是否定的&#xff0c;本文将从源码的角度来分享一下 spring security oauth2 的解析过程&#xff0c;及其扩展点的应用场景。Token 解析过程说明当我们使用 spring security oauth2 时, 一般情况下需要把认证中心申请的 token …

Map.getOrDefault()方法

default V getOrDefault(Object key, V defaultValue) {V v;return (((v get(key)) ! null) || containsKey(key))? v: defaultValue;}这是源码&#xff0c;意思就是当Map集合中有这个key时&#xff0c;就使用这个key对应的value值&#xff0c;如果没有这个key就使用默认值de…

java开发原则_java开发中,大家处理异常的原则是什么,是如何处理的?

展开全部最熟悉的陌生人&#xff1a;异常异常的类e5a48de588b63231313335323631343130323136353331333361326365型Throwable— Exception—- RuntimeException— Error需要注意的是&#xff0c;RuntimeException及其子类不需要在方法签名中显示注明异常抛出。例如&#xff1a;v…

java8合并两个Map

合并两个Map map自己的方法 实现方式是通过 putAll() 方法将多个 map 对象中的数据放到另外一个全新的 map 对象中&#xff0c;代码如下所示&#xff0c;展示了两个 map 对象的合并&#xff0c;如果是多个 map 合并也是用这种方式。 public static void main(String[] args) {…

java 线程 spring_java中spring里实现多线程

Spring通过任务执行器(TaskExecutor)来实现多线程和并发编程的可使用ThreadPoolTaskExecutor来实现基于线程池的TaskExecutor在实际开发中由于多是异步&#xff0c;所以使用EnableAsync来支持异步任务&#xff0c;且要在Bean的方法中使用Async来声明其是一个异步任务?????…

java8 Map新增方法的使用

文章目录 文章目录文章目录java8 Map新增方法的使用概述1、compute()1、使用2、源码实现2、computeIfAbsent()1、使用2、源码3、computeIfPresent()4、merge()1、使用2、源码5、接下来简单介绍一下1.8之后Map添加的default方法map新增的方法&#xff1a;getOrDefaultforEachput…

java自动识别验证码_Java使用OCR技术识别验证码实现自动化登陆方法

活动介绍缘起是 GitChat 作者群有一位作者提出是否应该定期组织一些写作活动&#xff0c;活跃一下社区氛围&#xff0c;刚好 GitChat 内容组的小伙伴们也有这个想法&#xff0c;既然想法碰到一起&#xff0c;那就说做就做。既然是第一期&#xff0c;那么我们就定一个比较广的主…

出现503错误 怎么办

展开全部 出现503错误原因及解决办法 原因&#xff1a;web服务器不能处理HTTP请求&#xff0c;可能是临时超载或者是服务器进行维护。 解决办法&#xff1a;用户需要等待服务器的临时处理。在这种状态下&#xff0c;一些服务器可以简单的拒绝socket连接&#xff0c;否则会发…

sql语句优化总结 mysql_MySQL-SQL优化总结

转载:https://blog.csdn.net/qq_39390545/article/details/107020686理解SQL优化原理 &#xff0c;首先要搞清楚SQL执行顺序&#xff1a;SELECT语句 - 语法顺序:SELECTDISTINCT FROM JOIN ON WHERE GROUP BY HAVING ORDER BY LIMIT SELECT语句 - 执行顺序&#xff1a;FROM# 选取…

java枚举类中字段有没有必要加final____枚举类字段 Field ‘xxx‘ may be ‘final‘

java枚举类中字段有没有必要加final 今天在写一个系统统一返回码的枚举类时候&#xff0c;突然想到一个问题&#xff0c;当不小心手抖给枚举类自动生成了set方法&#xff0c;而恰巧在用的地方不小心用了set方法&#xff0c;从而修改了code值&#xff0c;由于枚举类是天然单例&a…

calcite连接mysql_calcite简单入门

1 介绍Apache Calcite是一款开源的动态数据管理框架&#xff0c;它提供了标准的 SQL 语言、多种查询优化和连接各种数据源的能力&#xff0c;但不包括数据存储、处理数据的算法和存储元数据的存储库。Calcite 之前的名称叫做optiq&#xff0c;optiq 起初在 Hive 项目中&#xf…

MySQL数据库索引及失效场景

文章目录1. MySQL索引概述1.1 索引的概念1.2 索引的特点1.3 索引的分类1.4 索引的使用场景2. 索引失效场景2.1 索引失效9种场景2.2 索引失效场景总结3. 索引失效验证3.1 全值匹配3.2 最佳左前缀3.3 索引计算3.4 索引范围&#xff1a;索引列上不能有范围查询3.5 索引覆盖&#x…

getLong java_java.lang.Long.getLong()方法实例

全屏java.lang.Long.getLong(String nm) 方法确定具有指定名称的系统属性的long值。如果没有具有指定名称的属性&#xff0c;如果指定名称为空或null&#xff0c;或者该属性没有正确的数字格式&#xff0c;则返回null。声明以下是java.lang.Long.getLong()方法的声明public sta…

@JsonProperty注解解析

1. 概述 来源: JsonPrpperty是jackson包下的一个注解&#xff0c;详细路径(com.fasterxml.jackson.annotation.JsonProperty;)作用:JsonProperty用在属性上&#xff0c;将属性名称序列化为另一个名称。例子&#xff1a;public class Person{JsonProperty(value "name&qu…

java内部类为什么使用很少_java内部类有什么好处?为什么需要内部类?

提起Java内部类(Inner Class)可能很多人不太熟悉&#xff0c;实际上类似的概念在C里也有&#xff0c;那就是嵌套类(Nested Class)&#xff0c;关于这两者的区别与联系&#xff0c;在下文中会有对比。内部类从表面上看&#xff0c;就是在类中又定义了一个类(下文会看到&#xff…

jdk中提供的Collection、Collections、Collector、Collectors你分的清楚?

初次一看四个有点相似&#xff0c;而且有些时候一不小心还真有可能敲错&#xff0c;因为喜欢代码提示没仔细看提示&#xff0c;结果通过.去调用结果发现没有找到你想用的方法。所以写代码的时候需要注意一点这个区别 Collections.emptyList(); Collectors.toMap(......);//所在…

java swing panel问题_关于 Java swing Box 的使用问题

代码import javax.swing.*;import java.awt.*;public class C5Ex1_2 {final static int WIDTH 400;final static int HEIGHT 400;public C5Ex1_2() {JFrame jf new JFrame("program 1");jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);jf.setSize(WIDTH, HEI…

SpringMVC注解@RequestParam全面解析____ 注解@RequestParam如何使用加与不加的区别

SpringMVC注解RequestParam全面解析 在此之前&#xff0c;写项目一直用的是RequestParam&#xff08;value“aa” requiredfalse&#xff09;这个注解&#xff0c;但是并不知道它的意思。现在懂了&#xff0c;特来记录下。 1、可以对传入参数指定参数名 1 RequestParam Stri…

java编写流星_纯Java代码实现流星划过天空

废话不多说了&#xff0c;直接给大家贴java代码了。import java.awt.Color;import java.awt.Graphics;import java.awt.image.BufferedImage;import javax.swing.JFrame;import javax.swing.JPanel;public class MeteorFly extends JFrame {final int MAX ; // (~)流星的个数f…

@requestbody和@requestparam到底什么作用

1、什么都不写 GET 可以自动封装为对象模型&#xff0c;没有的数值自动为0值 POST 请求体里面放了数据&#xff0c;但是还是使用了RequestParam里的数据 总结&#xff1a; 在不使用注解的情况下&#xff0c;相当于默认使用了RequestParam里的数据 &#xff08;这种理解是错…