1. 营销策划师首页
  2. 产品文案策划

缓存 缓存MyBatis中岂能少得了缓存?中的缓存是如何实现的

缓存 缓存MyBatis中岂能少得了缓存?中的缓存是如何实现的可以看到,打印了2次,没有用到缓存,也就是不同SqlSession中不能共享一级缓存。首先让我们来想一想,既然一级缓存的作用域只对同一个SqlSession有效,那么一级缓存应该存储在哪里比较合适是呢?PS:利用插件我们也可以自己去将缓存清除,后面我们会介绍插件相关知识。

前言

在计算机的世界中,缓存无处不在,操作系统有操作系统的缓存,数据库也会有数据库的缓存,各种中间件如Redis也是用来充当缓存的作用,编程语言中又可以利用内存来作为缓存。自然的,作为一款优秀的ORM框架,MyBatis中又岂能少得了缓存,那么本文的目的就是带领大家一起探究一下MyBatis的缓存是如何实现的。给我五分钟,带你彻底掌握MyBatis的缓存工作原理

为什么要缓存

在计算机的世界中,CPU的处理速度可谓是一马当先,远远甩开了其他操作,尤其是I/O操作,除了那种CPU密集型的系统,其余大部分的业务系统性能瓶颈最后或多或少都会出现在I/O操作上,所以为了减少磁盘的I/O次数,那么缓存是必不可少的,通过缓存的使用我们可以大大减少I/O操作次数,从而在一定程度上弥补了I/O操作和CPU处理速度之间的鸿沟。而在我们ORM框架中引入缓存的目的就是为了减少读取数据库的次数,从而提升查询的效率。

MyBatis缓存

MyBatis中的缓存相关类都在cache包下面,而且定义了一个顶级接口Cache,默认只有一个实现类PerpetualCache,PerpetualCache中是内部维护了一个HashMap来实现缓存

下图就是MyBatis中缓存相关类:

需要注意的是decorators包下面的所有类也实现了Cache接口,那么为什么我还是要说Cache只有一个实现类呢?其实看名字就知道了,这个包里面全部是装饰器,也就是说这其实是装饰器模式的一种实现。

我们随意打开一个装饰器:

可以看到,最终都是调用了delegate来实现,只是将部分功能做了增强,其本身都需要依赖Cache的唯一实现类PerpetualCache(因为装饰器内需要传入Cache对象,故而只能传入PerpetualCache对象,因为接口是无法直接new出来传进去的)。

在MyBatis中存在两种缓存,即一级缓存和二级缓存

一级缓存

一级缓存也叫本地缓存,在MyBatis中,一级缓存是在会话(SqlSession)层面实现的,这就说明一级缓存作用范围只能在同一个SqlSession中,跨SqlSession是无效的。

MyBatis中一级缓存是默认开启的,不需要任何配置。

我们先来看一个例子验证一下一级缓存是不是真的存在,作用范围又是不是真的只是对同一个SqlSession有效。

一级缓存真的存在吗

package com.lonelyWolf.mybatis;import com.lonelyWolf.mybatis.mapper.UserAddressMapper;import com.lonelyWolf.mybatis.mapper.UserMapper;import com.lonelyWolf.mybatis.model.LwUser;

缓存_三级缓存 总缓存_缓存大师缓存

import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import java.io.IOException; import java.io.InputStream; import java.util.List; public class TestMyBatisCache { public static void main(String[] args) throws IOException { String resource = "mybatis-config.xml"; //读取mybatis-config配置文件 InputStream inputStream = Resources.getResourceAsStream(resource); //创建SqlSessionFactory对象 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); //创建SqlSession对象 SqlSession session = sqlSessionFactory.openSession(); UserMapper userMapper = session.getMapper(UserMapper.class); List<LwUser> userList = userMapper.selectUserAndJob(); List<LwUser> userList2 = userMapper.selectUserAndJob(); } }

三级缓存 总缓存_缓存大师缓存_缓存

执行后,输出结果如下:

我们可以看到,sql语句只打印了一次,这就说明第2次用到了缓存,这也足以证明一级缓存确实是存在的而且默认就是是开启的。

一级缓存作用范围

现在我们再来验证一下一级缓存是否真的只对同一个SqlSession有效,我们对上面的示例代码进行如下改变:

SqlSession session1 = sqlSessionFactory.openSession(); SqlSession session2 = sqlSessionFactory.openSession(); UserMapper userMapper1 = session1.getMapper(UserMapper.class); UserMapper userMapper2 = session2.getMapper(UserMapper.class); List<LwUser> userList =  userMapper1.selectUserAndJob(); List<LwUser> userList2 =  userMapper2.selectUserAndJob();

这时候再次运行,输出结果如下:

可以看到缓存,打印了2次,没有用到缓存,也就是不同SqlSession中不能共享一级缓存

一级缓存原理分析

首先让我们来想一想,既然一级缓存的作用域只对同一个SqlSession有效,那么一级缓存应该存储在哪里比较合适是呢?

是的,自然是存储在SqlSession内是最合适的,那我们来看看SqlSession的唯一实现类DefaultSqlSession:

DefaultSqlSession中只有5个成员属性,后面3个不用说,肯定不可能用来存储缓存,然后Configuration又是一个全局的配置文件,也不合适存储一级缓存,这么看来就只有Executor比较合适了,因为我们知道缓存,SqlSession只提供对外接口,实际执行sql的就是Executor。

既然这样,那我们就进去看看Executor的实现类BaseExecutor:

看到果然有一个localCache。而上面我们有提到PerpetualCache内缓存是用一个HashMap来存储缓存的,那么接下来大家肯定就有以下问题:

接下来就让我们逐一分析

缓存大师缓存_三级缓存 总缓存_缓存

一级缓存CacheKey的构成

既然缓存那么肯定是针对的查询语句,一级缓存的创建就是在BaseExecutor中的query方法内创建的:

createCacheKey这个方法的代码就不贴了,在这里我总结了一下CacheKey的组成,CacheKey主要是由以下6部分组成

一级缓存的使用

创建完CacheKey之后,我们继续进入query方法:

可以看到,在查询之前就会去localCache中根据CacheKey对象来获取缓存,获取不到才会调用后面的queryFromDatabase方法

一级缓存的创建

queryFromDatabase方法中会将查询得到的结果存储到localCache中

一级缓存什么时候会被清除

一级缓存的清除主要有以下两个地方:

PS:利用插件我们也可以自己去将缓存清除,后面我们会介绍插件相关知识。

二级缓存

一级缓存因为只能在同一个SqlSession中共享,所以会存在一个问题,在分布式或者多线程的环境下,不同会话之间对于相同的数据可能会产生不同的结果,因为跨会话修改了数据是不能互相感知的,所以就有可能存在脏数据的问题,正因为一级缓存存在这种不足,所以我们需要一种作用域更大的缓存,这就是二级缓存

二级缓存的作用范围

一级缓存作用域是SqlSession级别,所以它存储的SqlSession中的BaseExecutor之中,但是二级缓存目的就是要实现作用范围更广,那肯定是要实现跨会话共享的,在MyBatis中二级缓存的作用域是namespace,也就是作用范围是同一个命名空间,所以很显然二级缓存是需要存储在SqlSession之外的,那么二级缓存应该存储在哪里合适呢?

在MyBatis中为了实现二级缓存,专门用了一个装饰器来维护,这就是我们上一篇文章介绍Executor时还留下的没有介绍的一个对象:CachingExecutor。

如何开启二级缓存

二级缓存相关的配置有三个地方:

1、mybatis-config中有一个全局配置属性,这个不配置也行,因为默认就是true。

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

想详细了解mybatis-config的可以点击这里。

发表评论

邮箱地址不会被公开。 必填项已用*标注

联系我们

400-800-8888

在线咨询:点击这里给我发消息

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息