MyBatis 缓存模块

文章目录

  • 前言
  • 缓存的实现
    • Cache接口
    • PerpetualCache
  • 缓存的应用
    • 缓存对应的初始化
    • 一级缓存
    • 二级缓存
    • 第三方缓存

前言

MyBatis作为一个强大的持久层框架,缓存是其必不可少的功能之一,Mybatis中的缓存分为一级缓存和二级缓存。但本质上是一样的,都是使用Cache接口实现的。

缓存的实现

Cache接口

Cache接口是缓存模块中最核心的接口,它定义了所有缓存的基本行为。其实现类很多,但是大部分都是装饰器,只有PerpetualCache提供了Cache接口的基本实现。

image.png

PerpetualCache

PerpetualCache在缓存模块中扮演了ConcreteComponent的角色,其实现比较简单,底层使用HashMap记录缓存项,具体的实现如下:

/*** 在装饰器模式用 用来被装饰的对象* 缓存中的  基本缓存处理的实现* 其实就是一个 HashMap 的基本操作* @author Clinton Begin*/
public class PerpetualCache implements Cache {private final String id; // Cache 对象的唯一标识// 用于记录缓存的Map对象private final Map<Object, Object> cache = new HashMap<>();public PerpetualCache(String id) {this.id = id;}@Overridepublic String getId() {return id;}@Overridepublic int getSize() {return cache.size();}@Overridepublic void putObject(Object key, Object value) {cache.put(key, value);}@Overridepublic Object getObject(Object key) {return cache.get(key);}@Overridepublic Object removeObject(Object key) {return cache.remove(key);}@Overridepublic void clear() {cache.clear();}@Overridepublic boolean equals(Object o) {if (getId() == null) {throw new CacheException("Cache instances require an ID.");}if (this == o) {return true;}if (!(o instanceof Cache)) {return false;}Cache otherCache = (Cache) o;// 只关心IDreturn getId().equals(otherCache.getId());}@Overridepublic int hashCode() {if (getId() == null) {throw new CacheException("Cache instances require an ID.");}// 只关心IDreturn getId().hashCode();}}

cache.decorators包下提供的装饰器,他们都实现了Cache接口。这些装饰器都在PerpetualCache的基础上提供了一些额外的功能,通过多个组合实现一些特殊的需求。

其他的具体实现类,可以自行查阅。

缓存实现类描述作用装饰条件
基本缓存缓存基本实现类默认是PerpetualCache,也可以自定义比如RedisCache、EhCache等,具备基本功能的缓存类
LruCacheLRU策略的缓存当缓存到达上限时候,删除最近最少使用的缓存(Least Recently Use)eviction=“LRU”(默认)
FifoCacheFIFO策略的缓存当缓存到达上限时候,删除最先入队的缓存eviction=“FIFO”
SoftCacheWeakCache带清理策略的缓存通过JVM的软引用和弱引用来实现缓存,当JVM内存不足时,会自动清理掉这些缓存,基于SoftReference和WeakReferenceeviction="SOFT"eviction=“WEAK”
LoggingCache带日志功能的缓存比如:输出缓存命中率基本
SynchronizedCache同步缓存基于synchronized关键字实现,解决并发问题基本
BlockingCache阻塞缓存通过在get/put方式中加锁,保证只有一个线程操作缓存,基于Java重入锁实现blocking=true
SerializedCache支持序列化的缓存将对象序列化以后存到缓存中,取出时反序列化readOnly=false(默认)
ScheduledCache定时调度的缓存在进行get/put/remove/getSize等操作前,判断缓存时间是否超过了设置的最长缓存时间(默认是一小时),如果是则清空缓存–即每隔一段时间清空一次缓存flushInterval不为空
TransactionalCache事务缓存在二级缓存中使用,可一次存入多个缓存,移除多个缓存在TransactionalCacheManager中用Map维护对应关系

缓存的应用

缓存对应的初始化

在Configuration初始化的时候会为各种Cache实现注册对应的别名

image.png

在解析settings标签的时候,设置的默认值有如下

image.png

cacheEnabled默认为true,localCacheScope默认为 SESSION

在解析映射文件的时候会解析相关的cache标签

image.png

然后解析映射文件的cache标签后会在Configuration对象中添加对应的数据

  private void cacheElement(XNode context) {// 只有 cache 标签不为空才解析if (context != null) {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);}}

image.png

如果存储 cache 标签,那么对应的 Cache对象会被保存在 currentCache 属性中,保存在了 MapperStatement 对象的 cache 属性中。

在openSession的时创建对应的执行器的时候会有缓存的操作

  public Executor newExecutor(Transaction transaction, ExecutorType executorType) {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 {// 默认 SimpleExecutorexecutor = new SimpleExecutor(this, transaction);}// 二级缓存开关,settings 中的 cacheEnabled 默认是 trueif (cacheEnabled) {executor = new CachingExecutor(executor);}// 植入插件的逻辑,至此,四大对象已经全部拦截完毕executor = (Executor) interceptorChain.pluginAll(executor);return executor;}

如果 cacheEnabled 为 true 就会通过 CachingExecutor 来装饰executor 对象,在执行SQL操作的时候会涉及到缓存的具体使用。这个就分为一级缓存和二级缓存.

一级缓存

一级缓存也叫本地缓存(Local Cache),MyBatis的一级缓存是在会话(SqlSession)层面进行缓存的。MyBatis的一级缓存是默认开启的,不需要任何的配置(如果要关闭,localCacheScope设置为STATEMENT)。在BaseExecutor对象的query方法中有关闭一级缓存的逻辑

image.png

因为一级缓存是Session级别的缓存,肯定需要在Session范围内创建,其实PerpetualCache的实例化是在BaseExecutor的构造方法中创建的

  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;}

一级缓存的具体实现也是在BaseExecutor的query方法中来实现的

public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {// 异常体系之 ErrorContextErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());if (closed) {throw new ExecutorException("Executor was closed.");}if (queryStack == 0 && ms.isFlushCacheRequired()) {// flushCache="true"时,即使是查询,也清空一级缓存clearLocalCache();}List<E> list;try {// 防止递归查询重复处理缓存queryStack++;// 查询一级缓存// ResultHandler 和 ResultSetHandler的区别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--;}if (queryStack == 0) {for (DeferredLoad deferredLoad : deferredLoads) {deferredLoad.load();}// issue #601deferredLoads.clear();if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {// issue #482clearLocalCache();}}return list;}

一级缓存的验证:

同一个Session中的多个相同操作

    @Testpublic void test1() throws  Exception{// 1.获取配置文件InputStream in = Resources.getResourceAsStream("mybatis-config.xml");// 2.加载解析配置文件并获取SqlSessionFactory对象SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);// 3.根据SqlSessionFactory对象获取SqlSession对象SqlSession sqlSession = factory.openSession();// 4.通过SqlSession中提供的 API方法来操作数据库List<User> list = sqlSession.selectList("com.mapper.UserMapper.selectUserList");System.out.println(list.size());// 一级缓存测试System.out.println("---------");list = sqlSession.selectList("com.mapper.UserMapper.selectUserList");System.out.println(list.size());// 5.关闭会话sqlSession.close();}

通过日志的输出结果会看到第二次selectList没有进行sql查询。不同的Session中的相同操作,一级缓存是没有起作用的。

二级缓存

二级缓存是用来解决一级缓存不能跨会话共享的问题的,范围是namespace级别的,可以被多个SqlSession共享。

二级缓存的设置,首先是settings中的cacheEnabled要设置为true,默认的就是为true,这个步骤决定了在创建Executor对象的时候是否通过CachingExecutor来装饰。

image.png

设置了cacheEnabled标签为true后还需要在对应的映射文件中添加 cache 标签才行。

<!-- 声明这个namespace使用二级缓存 -->
<cache type="org.apache.ibatis.cache.impl.PerpetualCache"size="1024"  <!—最多缓存对象个数,默认1024-->eviction="LRU" <!—回收策略-->flushInterval="120000" <!—自动刷新时间 ms,未配置时只有调用时刷新-->readOnly="false"/> <!—默认是false(安全),改为true可读写时,对象必须支持序列化 -->

cache属性详解:

属性含义取值
type缓存实现类需要实现Cache接口,默认是PerpetualCache,可以使用第三方缓存
size最多缓存对象个数默认1024
eviction回收策略(缓存淘汰算法)LRU – 最近最少使用的:移除最长时间不被使用的对象(默认)。FIFO – 先进先出:按对象进入缓存的顺序来移除它们。SOFT – 软引用:移除基于垃圾回收器状态和软引用规则的对象。WEAK – 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。
flushInterval定时自动清空缓存间隔自动刷新时间,单位 ms,未配置时只有调用时刷新
readOnly是否只读true:只读缓存;会给所有调用者返回缓存对象的相同实例。因此这些对象不能被修改。这提供了很重要的性能优势。false:读写缓存;会返回缓存对象的拷贝(通过序列化),不会共享。这会慢一些,但是安全,因此默认是 false。改为false可读写时,对象必须支持序列化。
blocking启用阻塞缓存通过在get/put方式中加锁,保证只有一个线程操作缓存,基于Java重入锁实现

这样的设置表示当前的映射文件中的相关查询操作都会触发二级缓存,但如果某些个别方法我们不希望走二级缓存可以在标签中添加一个 useCache=false 来实现的设置不使用二级缓存。

image.png

当执行的对应的DML操作,在MyBatis中会清空对应的二级缓存和一级缓存。

  private void flushCacheIfRequired(MappedStatement ms) {Cache cache = ms.getCache();// 增删改查的标签上有属性:flushCache="true" (select语句默认是false)// 一级二级缓存都会被清理if (cache != null && ms.isFlushCacheRequired()) {tcm.clear(cache);}}

第三方缓存

在实际开发的时候我们一般也很少使用MyBatis自带的二级缓存,这时我们会使用第三方的缓存工具Ehcache或者Redis来实现。

实现步骤:

添加依赖

<dependency><groupId>org.mybatis.caches</groupId><artifactId>mybatis-redis</artifactId><version>1.0.0-beta2</version>
</dependency>

添加redis的属性文件

host=192.168.0.10
port=6379
connectionTimeout=5000
soTimeout=5000
database=0

加上Cache标签的配置

  <cache type="org.mybatis.caches.redis.RedisCache"eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/>

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

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

相关文章

【异常错误】detected dubious ownership in repository ****** is owned by: ‘

今天在github git的时候&#xff0c;突然出现了这种问题&#xff0c;下面的框出的部分一直显示&#xff1a; detected dubious ownership in repository at D:/Pycharm_workspace/SBDD/1/FLAG D:/Pycharm_workspace/SBDD/1/FLAG is owned by: S-1-5-32-544 but the current use…

多线程的学习第二篇

多线程 线程是为了解决并发编程引入的机制. 线程相比于进程来说,更轻量 ~~ 更轻量的体现: 创建线程比创建进程,开销更小销毁线程比销毁进程,开销更小调度线程比调度进程,开销更小 进程是包含线程的. 同一个进程里的若干线程之间,共享着内存资源和文件描述符表 每个线程被独…

外国固定资产管理系统功能有哪些

很多公司都在寻找提高自己资产管理效益的方法。为了满足这一要求&#xff0c;国外的固定资产管理系统已经发展成多种形式。以下是国外一些常见的固定资产管理系统的特点:自动化和智能化:许多现代固定资产管理系统采用自动化和数字化技术&#xff0c;以简化流程&#xff0c;减少…

网络编程day04(网络属性函数、广播、组播、TCP并发)

今日任务 对于newfd的话&#xff0c;最好是另存然后传入给分支线程&#xff0c;避免父子线程操作同一个文件描述符 1.广播&#xff1a; 接收端 代码&#xff1a; #include <stdio.h> #include <string.h> #include <stdlib.h> #include <sys/types.h&…

Databend 开源周报第 111 期

Databend 是一款现代云数仓。专为弹性和高效设计&#xff0c;为您的大规模分析需求保驾护航。自由且开源。即刻体验云服务&#xff1a;https://app.databend.cn 。 Whats On In Databend 探索 Databend 本周新进展&#xff0c;遇到更贴近你心意的 Databend 。 理解 SHARE END…

大数据(九):数据可视化(一)

专栏介绍 结合自身经验和内部资料总结的Python教程&#xff0c;每天3-5章&#xff0c;最短1个月就能全方位的完成Python的学习并进行实战开发&#xff0c;学完了定能成为大佬&#xff01;加油吧&#xff01;卷起来&#xff01; 全部文章请访问专栏&#xff1a;《Python全栈教…

Linux_9_网络协议和管理

目录 1网络基础1.1 网络概念1.2常见的网络物理组件1.3网络应用程序1.3.1各种网络应用1.3.2应用程序对网络的要求 1.4网络的特征1.4.1速度(带宽)1.4.2网络拓扑 1.5网络1.5.1网络准和分层1.5.2开放系统互联OSI1.5.3网络的通信过程1.5.3.1数据封装和数据解封1.5.3.2协议数据单元PD…

基于javaweb的顶岗实习管理系统(jsp+servlet)

系统简介 本项目采用eclipse工具开发&#xff0c;jspservletjquery技术编写&#xff0c;数据库采用的是mysql&#xff0c;navicat开发工具。 三个角色&#xff1a;管理员&#xff0c;教师&#xff0c;学生 模块简介 管理员&#xff1a; 1、登录 2、学生管理 3、公告管理 …

如何通过一键导出导入数据实现批量重命名文件名称

在日常办公中&#xff0c;我们经常需要对大量的文件进行重命名&#xff0c;以便更好地管理和查找文件。而且&#xff0c;有时候我们还需要将文件名称翻译成其他语言&#xff0c;以适应不同的工作需求。如何高效地完成这项任务呢&#xff1f;接下来&#xff0c;我将介绍一种方法…

【李沐深度学习笔记】线性代数实现

课程地址和说明 线性代数实现p2 本系列文章是我学习李沐老师深度学习系列课程的学习笔记&#xff0c;可能会对李沐老师上课没讲到的进行补充。 这节就算之前内容的复习&#xff0c;后面以截图形式呈现 标量由只有一个元素的张量表示 import torch x torch.tensor([3.0]) y …

Spring Boot魔法:简化Java应用的开发与部署

文章目录 什么是Spring Boot&#xff1f;1. 自动配置&#xff08;Auto-Configuration&#xff09;2. 独立运行&#xff08;Standalone&#xff09;3. 生产就绪&#xff08;Production Ready&#xff09;4. 大量的起步依赖&#xff08;Starter Dependencies&#xff09; Spring …

免杀对抗-java语言-shellcode免杀-源码修改+打包exe

JAVA-ShellCode免杀-源码修改&打包EXE Shellcode-生成/上线 1.msf生成shellcode 命令&#xff1a;msfvenom -p java/meterpreter/reverse_tcp LHOSTx.x.x.x LPORTxxxx -f jar -o msf.jar 2.msf设置监听 3.执行msf生成的shellcode jar包&#xff0c;成功上线 命令&#xff1…

05_Bootstrap插件02

7 小标签 通过 .label 实现小标签&#xff0c;用于提示类。 <h1>h1标题 <span class"label label-default">标签</span></h1> <h2>h2标题<span class"label label-default">标签</span></h2> <h3&g…

Uni-app 调用微信地图导航功能【有图】

前言 我们在使用uni-app时&#xff0c;有时候会遇到需要开发地图和导航的功能&#xff0c;这些方法其实微信小程序的API已经帮我们封装好了 详见&#xff1a;微信小程序开发文档 接下来我们就演示如何用uni-app来使用他们 使用 <template><view><button type…

5G通信与蜂窝模组之间的关系

5G通信是第五代移动通信技术的简称&#xff0c;它代表了一种新一代的无线通信技术标准。5G通信的主要目标是提供更高的数据传输速度、更低的延迟、更大的网络容量以及更可靠的连接&#xff0c;以支持各种新兴应用和服务&#xff0c;包括高清视频流、虚拟现实、物联网&#xff0…

Mybatis学习笔记11 缓存相关

Mybatis学习笔记10 高级映射及延迟加载_biubiubiu0706的博客-CSDN博客 缓存:cache 缓存的作用:通过减少IO的方式,来提高程序的执行效率 Mybatis的缓存:将select语句的查询结果放到缓存(内存)当中,下一次还是这条select语句的话,直接从缓存中取,不再查数据库.一方面是减少了I…

【Vue3 源码讲解】nextTick

nextTick 是 Vue 3 中用于异步执行回调函数的函数&#xff0c;它会将回调函数延迟到下一个微任务队列中执行。其中&#xff0c;Vue 更新 DOM 是异步的。下面是对 nextTick 函数的详细解释&#xff1a; export function nextTick<T void, R void>(this: T,fn?: (this:…

websocket php教程

WebSocket 是 HTML5 提供的一种网络通讯协议&#xff0c;用于服务端与客户端实时数据传输。广泛用于浏览器与服务器的实时通讯&#xff0c;APP与服务器的实时通讯等场景。 相比传统HTTP协议请求响应式通讯&#xff0c;WebSocket协议可以做到实时的双向通讯&#xff0c;服务端可…

Webpack打包图片

一、在js文件中引入图片 二、在package.config.js中配置加载器 module.exports {mode: "production", // 设置打包的模式&#xff1a;production生产模式 development开发模式module: {rules: [// 配置img加载器{test: /\.(jpg|png|gif)$/i,type:"asset/resou…

Hadoop-sqoop

sqoop 1. Sqoop简介及原理 简介&#xff1a; Sqoop是一款开源的工具,主要用于在Hadoop(Hive)与传统的数据库(mysq1.postgresql..)间进行数据的传递&#xff0c;可以将一个关系型数据库&#xff08;例如: MySQL ,Oracle ,Postgres等&#xff09;中的数据导进到Hadoop 的HDFS中&…