Mybatis常用用法之 Mybatis缓存

这是我参与更文挑战的第2天,活动详情查看: 更文挑战

一:定义

缓存就是内存中的数据,常常来自对数据库查询结果的保存,使用缓存,我们可以避免频繁的与数据库
进行交互,进而提高响应速度

Mybatis也支持数据的缓存,分为一级缓存和二级缓存,可以通过下图理解一级缓存和二级缓存的作用范围:

image.png

以上可以看出

  1. 一级缓存是sqlsession级别的缓存,直白的说就是单个sql语句的缓存,在操作数据库时,需要构建sqlsession对象,在对象中有一个数据结构(HashMap)用户存储缓存数据,不同的sqlsession之间的缓存互不影响
  2. 二级缓存是Mapper级别的缓存,直白的说就是一个xml文件的缓存,该缓存默认是关闭的,多个sqlsession去操作同一个Mapper中的SQL语句,共用一个缓存,该缓存是跨sqlsession的

二:一级缓存

当我们进行sql查询的时候,Mybatis会先到缓存中去查询该sql语句的缓存,如果该缓存已经存在,则直接返回缓存中的结果,不再进行数据库交互,当缓存中不存在该sql语句的缓存的时候,则会直接去数据库查询,获取结果后,会将结果进行缓存,并同时发送给用户
当用户执行commit操作(插入,删除,更新),则会清空sqlsession的一级缓存,目的是为了避免出现脏读。

三:二级缓存

二级缓存和一级缓存原理一样,第一次查询会将数据放入缓存,后续查询会从缓存中取,但是一级缓存是sqlsession级别,二级缓存是mapper级别的,也就是说,多个sqlsession可以共享一个mapper中的二级缓存,如果两个mapper的namespace相同,即使是不同文件的两个mapper,他们执行sql查询到的数据也维护在同一个二级缓存中。

image.png

四:如何使用二级缓存

由于二级缓存默认是关闭的,所以需要我们手动开启
1.首先,我们需要在全局配置文件sqlMapConfig.xml文件中加入如下代码:

<!--开启二级缓存--> 
<settings> 
    <setting name="cacheEnabled" value="true"/> 
</settings>
复制代码

然后再具体的Mapper.xml中开启缓存,添加如下配置

<cache></cache>
复制代码

我们可以看到mapper.xml文件中就这么一个空标签,其实这里可以配置,PerpetualCache这个类是
mybatis默认实现缓存功能的类。我们不写type就使用mybatis默认的缓存,也可以去实现Cache接口 来
自定义缓存。

开启了二级缓存后,还需要将要缓存的pojo实现Serializable接口,为了将缓存数据取出执行反序列化操
作,因为二级缓存数据存储介质多种多样,不一定只存在内存中,有可能存在硬盘中,如果我们要再取
这个缓存的话,就需要反序列化了。所以mybatis中的pojo都去实现Serializable接口

mybatis中还可以配置userCacheflushCache等配置项,userCache是用来设置是否禁用二级缓 存
的,在statement中设置useCache=false可以禁用当前select语句的二级缓存,即每次查询都会发出 sql
去查询,默认情况是true,即该sql使用二级缓存

<select id="selectUserByUserId" useCache="false" resultType="com.pojo.User" parameterType="int"> 
    select * from user where id=#{id} 
</select>
复制代码

这种情况是针对每次查询都需要最新的数据sql,要设置成useCache=false,禁用二级缓存,直接从数 据
库中获取。
在mapper的同一个namespace中,如果有其它insert、update, delete操作数据后需要刷新缓存,如
果不执行刷新缓存会出现脏读。
设置statement配置中的flushCache=”true”属性,默认情况下为true,即刷新缓存,如果改成false则 不
会刷新。使用缓存时如果手动修改数据库表中的查询数据会出现脏读。

<select id="selectUserByUserId" flushCache="true" useCache="false" resultType="com.pojo.User" parameterType="int"> 
    select * from user where id=#{id} 
</select>
复制代码

一般下执行完commit操作都需要刷新缓存,flushCache=true表示刷新缓存,这样可以避免数据库脏
读。所以我们不用设置,默认即可

五:二级缓存整和redis

上面我们介绍了 mybatis自带的二级缓存,但是这个缓存是单服务器工作,无法实现分布式缓存。 那么
什么是分布式缓存呢?一般分布式项目同一个服务都会部署多个,如果我们用mybatis默认的缓存,用户第一次访问的时候根据负载均衡可能会访问到服务器A,当继续第二次访问的时候,可能就会访问到服务器B,那么原先在服务器A上的缓存,我们是拿不到的,所以进行第二次访问的时候,还是需要直接访问数据库,这样就违背了我们用缓存存储的初衷,效果如下图所示:

image.png

为了解决这个问题,就得找一个分布式的缓存,专门用来存储缓存数据的,这样不同的服务器要缓存数
据都往它那里存,取缓存数据也从它那里取,如下图所示:

image.png

如上图所示,在几个不同的服务器之间,我们使用第三方缓存框架,将缓存都放在这个第三方框架中, 然
后无论有多少台服务器,我们都能从缓存中获取数据。
这里我们介绍mybatis与redis的整合。

实现:
pom文件:

<dependency> 
    <groupId>org.mybatis.caches</groupId> 
    <artifactId>mybatis-redis</artifactId> 
    <version>1.0.0-beta2</version> 
</dependency>
复制代码

配置文件:

<?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="com.mapper.IUserMapper"> 
<cache type="org.mybatis.caches.redis.RedisCache" /> 
<select id="findAll" resultType="com.pojo.User" useCache="true"> 
    select * from user 
</select>
复制代码

redis.properties:

redis.host=localhost 
redis.port=6379 
redis.connectionTimeout=5000 
redis.password= 
redis.database=0
复制代码

注意:这里的redis.properties为固定文件名称,不可变

为什么要名称固定为这个呢?接下来我们翻阅一下源码,找寻一下答案.

RedisCache和大家普遍实现Mybatis的缓存方案大同小异,无非是实现Cache接口,并使用jedis操作缓
存;

image.png

RedisCache在mybatis启动的时候,由MyBatis的CacheBuilder创建,创建的方式很简单,就是调用
RedisCache的带有String参数的构造方法,即RedisCache(String id);而在RedisCache的构造方法中,
调用了 RedisConfigu rationBuilder 来创建 RedisConfig 对象,并使用 RedisConfig 来创建JedisPool。
接下来我们进入 parseConfiguration()方法中继续分析。

image.png
我们可以看到 在该方法的第一行就更加一个路径,调用getResourceAsStream()方法,将文件加载为IO流,
显而易见,我们的配置文件路径肯定就是代码中对应的redisPropertiesFilename属性

我们在看一下redisPropertiesFilename属性对应的是什么值

image.png
我们看到 实际redisPropertiesFilename并没有指定什么值,再往下看,我们看到一个构造函数,在该构造函数中,我们发现,redisPropertiesFilename其实就是上面常量中所对应的值,而常量对应的值就是我们的 redis.properties,由此可以得出上面的结论。

本篇文章到此结束,希望对各位小伙伴有所帮助

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享