本文来说下Mybatis中的缓存

文章目录

  • Mybatis是什么
  • Mybatis的一级缓存
  • Mybatis的二级缓存
  • 本文小结

Mybatis是什么

Mybatis是一个半自动 ORM(对象关系映射)框架,它内部封装了JDBC,加载驱动、创建连接、创建 statement 等繁杂的过程,我们开发的时候只需要关注如何编写 SQL 语句,而不用关心其他的。

为什么说 Mybatis 是一个半自动 ORM 的框架呢?

ORM,是Object和Relation之间的映射,而Mybatis 在查询关联对象或关联集合对象时,需要手动编写 sql 来完成,所以,称之为半自动 ORM 框架,而Hibernate 属于全自动 ORM 映射工具,使用 Hibernate 查询关联对象或者关联集合对象时,可以根据对象关系模型直接获取,所以它是全自动的。


Mybatis的一级缓存

我们先说 Mybatis 的一级缓存,因为这是如果不手动配置,他是自己默认开启的一级缓存,一级缓存只是相对于同一个 SqlSession 而言,参数和SQL完全一样的情况下,我们使用同一个SqlSession对象调用一个Mapper方法,往往只执行一次SQL,因为使用SelSession第一次查询后,MyBatis会将其放在缓存中,以后再查询的时候,如果没有声明需要刷新,并且缓存没有超时的情况下,SqlSession都会取出当前缓存的数据,而不会再次发送SQL到数据库。

当我们面试的时候,说完这个,一般情况下,面试官一定会追问下去,毕竟技术就是要问到你的知识盲区才会停止。

那我们就来画个图表示一下一级缓存


那面试官肯定会说,直接从数据库查不就行了,为啥要一级缓存呢?

当我们使用MyBatis开启一次和数据库的会话时, MyBatis 会创建出一个 SqlSession 对象表示一次与数据库之间的信息传递,在我们执行 SQL 语句的过程中,们可能会反复执行完全相同的查询语句,如果不采取一些措施,我们每一次查询都会查询一次数据库,而如果在极短的时间内做了很多次相同的查询操作,那么这些查询返回的结果很可能相同。

也就是说,如果我们在短时间内,频繁的去执行一条 SQL ,查询返回的结果本来应该是改变了,但是我们查询出来的时候,会出现结果一致的情况,正是为了解决这种问题,也为了减轻数据库的开销,所以 Mybatis 默认开启了一级缓存。


Mybatis的二级缓存

Mybatis 的二级缓存一般如果你不对他进行设置,他是不会开启的,而二级缓存是什么呢?Mybatis 中的二级缓存实际上就是 mapper 级别的缓存,而这时候肯定会有人说,那么不同之间的 Mapper 是同一个缓存么?

答案是否定的,他不是一个,Mapper 级别的缓存实际上就是相同的 Mapper 使用的是一个二级缓存,但是在二级缓存中,又有多个不同的 SqlSession ,而不同的 Mapper 之间的二级缓存也就是互相不会影响的。

就类似下面的图


这二级缓存是不是就看起来有点意思了?

那怎么能够开启二级缓存呢?

1.MyBatis配置文件

<settings><setting name = "cacheEnabled" value = "true" />
</settings>

2.MyBatis 要求返回的 POJO 必须是可序列化的

3.Mapper 的 xml 配置文件中加入 标签

既然我们想要了解这个二级缓存,那么必然,我们还得知道它里面的配置都有哪些含义。

我们先从标签看起,然后从源码里面看都有哪些配置信息提供给我们使用:


blocking : 直译就是调度,而在 Mybatis 中,如果缓存中找不到对应的 key ,是否会一直 blocking ,直到有对应的数据进入缓存。

eviction : 缓存回收策略

而缓存回收策略,在源码中是有直接体现的,那么他们分别都对应了什么形式呢?


typeAliasRegistry.registerAlias("PERPETUAL", PerpetualCache.class);
typeAliasRegistry.registerAlias("FIFO", FifoCache.class);
typeAliasRegistry.registerAlias("LRU", LruCache.class);
typeAliasRegistry.registerAlias("SOFT", SoftCache.class);
typeAliasRegistry.registerAlias("WEAK", WeakCache.class);
  • PERPETUAL : 选择 PERPETUAL 来命名缓存,暗示这是一个最底层的缓存,数据一旦存储进来,永不清除.好像这种缓存不怎么受待见。
  • FIFO : 先进先出:按对象进入缓存的顺序来移除它们
  • LRU : 最近最少使用的:移除最长时间不被使用的对象。
  • SOFT : 软引用:移除基于垃圾回收器状态和软引用规则的对象。
  • WEAK : 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。

大家虽然看着 PERPETUAL 排在了第一位,但是它可不是默认的,在 Mybatis 的缓存策略里面,默认的是 LRU 。

PERPETUAL :

源代码如下:

public class PerpetualCache implements Cache {private final String id;private Map<Object, Object> cache = new HashMap<>();public PerpetualCache(String id) {this.id = id;}

恩?看着是不是有点眼熟,它怎么就只是包装了 HashMap ? 你还别奇怪,他还真的就是使用的 HashMap ,不得不说,虽然人家是使用的 HashMap ,但是那可是比咱们写的高端多了。

既然使用 HashMap ,那么必然就会有Key,那么他们的Key是怎么设计的?

CacheKey:

public class CacheKey implements Cloneable, Serializable {private static final long serialVersionUID = 1146682552656046210L;public static final CacheKey NULL_CACHE_KEY = new NullCacheKey();private static final int DEFAULT_MULTIPLYER = 37;private static final int DEFAULT_HASHCODE = 17;private final int multiplier;private int hashcode; //用于表示CacheKey的哈希码private long checksum; //总和校验,当出现复合key的时候,分布计算每个key的哈希码,然后求总和private int count;//当出现复合key的时候,计算key的总个数// 8/21/2017 - Sonarlint flags this as needing to be marked transient.  While true if content is not serializable, this is not always true and thus should not be marked transient.private List<Object> updateList;//当出现复合key的时候,保存每个key

FIFO: 先进先出缓冲淘汰策略

public class FifoCache implements Cache {private final Cache delegate; //被装饰的Cache对象private final Deque<Object> keyList;//用于记录key 进入缓存的先后顺序private int size;//记录了缓存页的上限,超过该值需要清理缓存(FIFO)public FifoCache(Cache delegate) {this.delegate = delegate;this.keyList = new LinkedList<>();this.size = 1024;}

在 FIFO 淘汰策略中使用了 Java 中的 Deque,而 Deque 一种常用的数据结构,可以将队列看做是一种特殊的线性表,该结构遵循的先进先出原则。Java中,LinkedList实现了Queue接口,因为LinkedList进行插入、删除操作效率较高。

当你看完这个源码的时候,是不是就感觉源码其实也没有那么难看懂,里面都是我们已经掌握好的知识,只不过中间做了一些操作,进行了一些封装。

LRU : 最近最少使用的缓存策略

而我们需要看的源码则是在 Mybatis 中的源码,

public class LruCache implements Cache {private final Cache delegate;private Map<Object, Object> keyMap;private Object eldestKey;//记录最少被使用的缓存项keypublic LruCache(Cache delegate) {this.delegate = delegate;setSize(1024);//重新设置缓存的大小,会重置KeyMap 字段 如果到达上限 则更新eldestKey}public void putObject(Object key, Object value) {delegate.putObject(key, value);// 删除最近未使用的keycycleKeyList(key);}

SOFT: 基于垃圾回收器状态和软引用规则的对象

在看到基于垃圾回收器的时候,竟然有GC的事情,那还不赶紧看看,来瞅瞅吧

public class SoftCache implements Cache {//在SoftCache 中,最近使用的一部分缓存项不会被GC回收,这就是通过将其value添加到private final Deque<Object> hardLinksToAvoidGarbageCollection;//引用队列,用于记录GC回收的缓存项所对应的SoftEntry对象private final ReferenceQueue<Object> queueOfGarbageCollectedEntries;//底层被修饰的Cache 对象private final Cache delegate;//连接的个数,默认是256private int numberOfHardLinks;public SoftCache(Cache delegate) {this.delegate = delegate;this.numberOfHardLinks = 256;this.hardLinksToAvoidGarbageCollection = new LinkedList<>();this.queueOfGarbageCollectedEntries = new ReferenceQueue<>();}public void putObject(Object key, Object value) {// 清除被GC回收的缓存项removeGarbageCollectedItems();// 向缓存中添加缓存项delegate.putObject(key, new SoftEntry(key, value, queueOfGarbageCollectedEntries));}public Object getObject(Object key) {Object result = null;// 查找对应的缓存项@SuppressWarnings("unchecked") // assumed delegate cache is totally managed by this cacheSoftReference<Object> softReference = (SoftReference<Object>) delegate.getObject(key);if (softReference != null) {result = softReference.get();// 已经被GC 回收if (result == null) {// 从缓存中清除对应的缓存项delegate.removeObject(key);} else {// See #586 (and #335) modifications need more than a read lock synchronized (hardLinksToAvoidGarbageCollection) {hardLinksToAvoidGarbageCollection.addFirst(result);if (hardLinksToAvoidGarbageCollection.size() > numberOfHardLinks) {hardLinksToAvoidGarbageCollection.removeLast();}}}}return result;}public void clear() {synchronized (hardLinksToAvoidGarbageCollection) {// 清理强引用集合hardLinksToAvoidGarbageCollection.clear();}// 清理被GC回收的缓存项removeGarbageCollectedItems();delegate.clear();}//其中指向key的引用是强引用,而指向value的引用是弱引用private static class SoftEntry extends SoftReference<Object> {private final Object key;SoftEntry(Object key, Object value, ReferenceQueue<Object> garbageCollectionQueue) {super(value, garbageCollectionQueue);this.key = key;}}

WEAK : 基于垃圾收集器状态和弱引用规则的对象

public class WeakCache implements Cache {private final Deque<Object> hardLinksToAvoidGarbageCollection;private final ReferenceQueue<Object> queueOfGarbageCollectedEntries;private final Cache delegate;private int numberOfHardLinks;public WeakCache(Cache delegate) {this.delegate = delegate;this.numberOfHardLinks = 256;this.hardLinksToAvoidGarbageCollection = new LinkedList<>();this.queueOfGarbageCollectedEntries = new ReferenceQueue<>();}

WeakCache在实现上与SoftCache几乎相同,只是把引用对象由SoftReference软引用换成了WeakReference弱引用。


本文小结

关于 Mybatis 的二级缓存,你了解了么?下次遇到面试官问这个的时候,你应该知道怎么成功(装杯)不被打了吧。

Mybatis中的缓存详解相关推荐

  1. MyBatis中@MapKey使用详解

    MyBatis中@MapKey使用详解 我们在上一篇文章中讲到在Select返回类型中是返回Map时,是对方法中是否存在注解@MapKey,这个注解我也是第一次看到,当时我也以为是纯粹的返回单个数据对 ...

  2. mybatis的一级缓存详解

    文章目录 1.mybatis一级缓存是什么 2.代码示例 2.1.结果展示: 2.2.现象解析 3.有多个SqlSession或者分布式的环境下,数据库写操作会引起脏数据 3.1代码如下: 3.2结果 ...

  3. Asp.Net中MVC缓存详解

    本文通过介绍了Asp.Net中MVC缓存的种类,以及他们之间的区别等内容,让学习者能够深入的了解MVC缓存的原理机制,以下是具体内容: 缓存是一种保存资源副本并在下次请求时直接使用该副本的技术.当 w ...

  4. mybatis 中 if-test 判断详解

    <if test="takeWay == '0'">]mybatis的if判断 单个的字符要写到双引号里面才行,改为<if test='takeWay == &q ...

  5. mybatis中foreach标签详解

    转载自:https://blog.csdn.net/gwd1154978352/article/details/75408498 foreach的主要用在构建in条件中,它可以在SQL语句中进行迭代一 ...

  6. MyBatis 一级缓存二级缓存详解

    相关内容: 架构师系列内容:架构师学习笔记(持续更新) MyBatis 缓存详解 cache 缓存 缓存是一般的ORM 框架都会提供的功能,目的就是提升查询的效率和减少数据库的压力.跟Hibernat ...

  7. Mybatis二级缓存详解

    Mybatis二级缓存 Mybatis相关全览 一.简介 二.一级缓存 1.入口 2.演示 案例一: 案例二: 3.总结 三.二级缓存 1.入口 2.如何开启二级缓存 cache-ref配置 cach ...

  8. Mybatis系列全解(四):全网最全!Mybatis配置文件XML全貌详解

    封面:洛小汐 作者:潘潘 做大事和做小事的难度是一样的.两者都会消耗你的时间和精力,所以如果决心做事,就要做大事,要确保你的梦想值得追求,未来的收获可以配得上你的努力. 前言 上一篇文章 <My ...

  9. Mybatis(三) 映射文件详解

    前面说了全局配置文件中内容的详解,大家应该清楚了,现在来说说这映射文件,这章就对输入映射.输出映射.动态sql这几个知识点进行说明,其中高级映射(一对一,一对多,多对多映射)在下一章进行说明. 一.输 ...

最新文章

  1. 什么样的程序员生涯指南,能在GitHub上获3.6万星
  2. 杭电多校第三场-H-Game
  3. 系统集成项目管理工程师-项目风险管理笔记
  4. Knative Serving 健康检查机制分析
  5. 八叉树 java_java简单实现八叉树图像处理代码示例
  6. 启明云端分享|乐鑫推出在线选型工具 ESP Product Selector
  7. 呕心沥血为小白总结13个学习网站-错过了你注定绕弯!
  8. Redis集群搭建笔记
  9. Web高效管理多个项目的SVN仓库
  10. 安卓线程同步面试_Android面试题
  11. 同学之间互相出的一些有趣题目
  12. php的mbstring模块安装折腾记录
  13. Spring Cloud Eureka 源码分析(一) 服务端启动过程
  14. 0322Private strand flush not complete
  15. redis之列表字典操作
  16. STM32用串口(USB串口)下载程序的方法
  17. windows(微软知识库)可以根据事件ID查看 各种事件
  18. 华为HCNA网络工程师【从入门到精通】自学视频[肖哥]-肖宗鹏-专题视频课程
  19. The remote end hung up unexpectedly The remote end hung up unexpectedly RPC failed;
  20. python3爬虫——模拟登录QQ邮箱

热门文章

  1. CentOS安装Redis、PHPredis扩展及Redis配置文件详解
  2. 我是不是在浪费生命?
  3. Iphone开发-NSdata 与 NSString,Byte数组,UIImage 的相互转换
  4. 问题管理——“斩草需除根”
  5. 配置Apache虚拟机
  6. HDU 4547 CD操作
  7. STL stack 容器
  8. Java中的ASCII码与Unicode码
  9. hdu 4055 hdu 4489 动态规划
  10. 分析器错误信息: 未能加载类型命名空间.类... ---小结