mybatis一级二级缓存

前言

mybatis作为一个ORM框架,缓存其实不是他的主要功能,作用也很有限。但要全面了解这个框架,这部分的知识也必不可少,下面就简单地总结下。

一级缓存

什么是一级缓存

一级缓存是SqlSession级别的,同一个SqlSession执行查询方法时会先从缓存中查询,如果查到就直接返回数据,不需要再查询数据库。缓存在SqlSession关闭时清空。当spring与mybatis整合后,每执行一个方法后,SqlSession都会关闭,这种情况一级缓存除了在事务下有用外,其它情况都没用了。

怎么用

mybatis一级缓存是默认开启的,在配置文件中添加如下语句就可以使用一级缓存,共有两个选项,SESSION和STATEMENT,默认是SESSION级别,即在一个Mybatis会话中执行的所有语句,都会共享这一个缓存。一种是STATEMENT级别,可以理解为缓存只对当前执行的这一个statement有效。

    <settings>
        <setting name="localCacheScope" value="SESSION" />
    </settings>

代码验证

在这里插入图片描述一级缓存生效,只查询数据库一次
在这里插入图片描述
在这里插入图片描述中间增加插入操作,一级缓存失效
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
设置一级缓存为STATEMENT级别后
在这里插入图片描述

源码讨论

开启debug,看下执行一次查询究竟经历了什么~~
在这里插入图片描述
1、我们调用mapper接口方法,底层调的是mybatis为我们生成的代理对象方法,然后调用MapperMethod的execute方法
在这里插入图片描述
2、MapperMethod的execute方法里主要是一个swicth,用来根据方法的类型执行不同的操作。调用SqlSession的SelectOne
在这里插入图片描述
3、默认是调用SqlSession的实现DefaultSqlSession,selectOne底层是调用 selectList
在这里插入图片描述4、selectList调用的是CashingExecutor(实现Executor接口)的query方法
在这里插入图片描述
5、query里又调了CashingExecutor另一个query重载方法
在这里插入图片描述
6、这里有一些缓存的操作,但这不是一级缓存,我们继续往下看,可以看到实际干活其实的是delegate(代表的意思,这里是SimpleExecutor,实现Executor)
在这里插入图片描述
7、SimpleExecutor继承BaseExecutor,一级缓存在BaseExecutor里实现,过程如下。这里可以看到如果我们在配置文件里配置一级缓存的级别为STATEMENT,就会清空缓存,相当于关闭一级缓存。
在这里插入图片描述
8、如果查询不到有缓存则会在queryFromDatabase里放入缓存
在这里插入图片描述
9、由于在DefaultSqlSession中insert,delete方法最后都是调用update,这里就以update为例,debug一下mapper的update方法,大概流程是一样,最后在执行BaseExecutor的update方法会清空缓存,所以在查询方法中间插入update方法,缓存会失效。
在这里插入图片描述

二级缓存

什么是二级缓存

二级缓存的范围比一级缓存要大,是namespace级别,同一个mapper文件下的方法共享。缺点就是当一个表有多个mapper文件操作时,很容易出现脏数据。

怎么用

mybatis的配置文件mybatis-config.xml里开启

   <!-- 全局配置 -->
    <settings>
        <setting name="cacheEnabled" value="true"/>
    </settings>

要开启的xxMapper.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="org.ysp.dao.UserDao">
    <cache/>

代码验证

为避免一级缓存影响,关闭一级缓存,开启二级缓存。
在这里插入图片描述
二级缓存生效,Cache Hit Ratio(缓存命中率)为0.5
在这里插入图片描述
中间加入修改操作
在这里插入图片描述
二级缓存失效
在这里插入图片描述
下面验证下二级缓存脏数据的情况。

增加一个ROLE表,在UserMapper下增加关联查询方法getRoleInfoByUser,在RoleMapper里增加修改方法update,验证如下
在这里插入图片描述可以看到查询出来了脏数据
在这里插入图片描述
在UserMapper里添加

<cache-ref namespace="org.ysp.mapper.RoleMapper"/>

如下图,UserMapper可以感知RoleMapper的修改。
在这里插入图片描述

源码讨论

这里讨论二级缓存的源码,有些细节将会被忽略,之后再深入研究。

二级缓存的代码在CachingExecutor的query方法里,这里得到一个SychronizedCache对象,由它来执行二级缓存。query调用TransactionCacheManager的getObject方法,SychronizedCache对象作为参数传入。
在这里插入图片描述在TransactionCacheManager里,又调用TransactionCache的getObject方法,SychronizedCache对象作为参数传入。
在这里插入图片描述
TransactionCache里,可以看到取缓存和放缓存不是同一个对象,这里让我困惑了很久。后来发现原来是在调用调用SqlSession的commit方法时,将entriesToAddOnCommit里的对象放入缓存。
在这里插入图片描述
SqlSession commit方法最后也是调用TransactionCache的commit方法。
在这里插入图片描述
下面再看下查询中间插入update方法时的源码

当调用CachingExecutor的update时,通过flushCacheIfRequired方法会设置一个标志位clearOnCommit,也就是上图SqlSession commit方法那个标志位,为true则清空缓存。
在这里插入图片描述
在这里插入图片描述
最后还有一个问题,二级缓存的SychronizedCache是被不同SqlSession共享的,那SychronizedCache是从哪里来的?其实也不难想到,只能在创建sqlSessionFactory的时候。
在这里插入图片描述

总结

1、网上很多文章都总结得很好了,这里就一句话,安全的使用mybatis一二级缓存的条件较高,还是让她做一个单纯的ORM框架就好了。

2、这里说下我趟过的坑,试验二级缓存的时候实体类要实现Serializable接口,不然运行会报错。还有记得查询后记得调用commit方法,不然二级缓存会失效。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值