# 本篇文章主要是为了帮助自己总结和加深理解,若能帮助到其他小伙伴也是极好的
基本介绍
Mybatis中支持一级缓存和二级缓存,一级缓存是默认开启的并且不能关闭,二级缓存默认关闭,可根据需要进行手动开启,总体来说Mybatis的一二级缓存的最终目的就是为了帮助数据库减轻压力,保证高效高速的查询,提高数据库的瓶颈。
一级缓存
一级缓存是基于hashmap的本地缓存,作用域只在session中,当session刷新或关闭后,这个session中所有的cache就会被清空。
注意 如果使用clearCache以及增、删、改的sql进行操作会导致select缓存被清空。
命中原则
mybatis是怎样判断两次select语句是完全相同的查询sql?
1、statementId
statementId其实就是select语句标签中的的id,如果两条select的id不同,那么必定不会命中。
例如图中代码,这是两条相同的select查询语句,先使用getByName01进行查询,再使用getByName02以及相同的参数进行查询,仍然不会进入缓存。
<select id="getByName01">
SELECT * FROM `user` WHERE name=#{name}
<select/>
<select id="getByName02">
SELECT * FROM `user` WHERE name=#{name}
<select/>
2、查询参数
若两次查询必须使用同一条statementId的sql,并且传入的参数一样,否则无法命中缓存。
这里要注意的是,mybatis不论你在java代码中使用何种方式进行传参,只要最终sql语句中获取的参数相等就可以判定为是相同的参数。
我们编写一条根据name查询的select,并且使用Map传参。
<select id="getByName" parameterType="java.util.Map" resultType="user">
SELECT * FROM `user` WHERE name=#{name}
<select/>
在两次查询中,我们传入两个不同的hashmap参数(只有name会传入sql中),结果会怎样?第二次查询使用缓存还是查询数据库?
public void test(){
// 第一次查询
HashMap param1 = new HashMap();
param1.put("name","alix");
param1.put("test",123);
mapper.getUserByName(param1);
// 第二次查询
HashMap param2 = new HashMap();
param2.put("name","alix");
param1.put("test",321);
mapper.getUserByName(param2);
}
通过测试执行,第二次查询竟然没有去数据库查询,而是读取了第一次查询的缓存,通过查看sql日志我们发现sql编译的结果如图所示。
==>> Preparing:SELECT * FROM user WHERE name=?
==>>Parameters:alix(Integer)
<<== Total:1
所以,参数相同指的是最终传递到sql中的参数只要是一样的就可以满足条件。
3、分页参数
这里的分页指的是mybatis自带的分页功能,很多人可能不熟悉或者很少使用,因为它是查询出数据库的所有数据到本地进行一个物理分页,而不是直接从数据库获取分页之后的结果,这样的一个操作执行效率很低,非常损耗性能,所以也不推荐使用(这里不做过多说明)。
4、sql语句
顾名思义,mybatis要求我们查询时使用的sql语句必须相同。但值得注意的是,这里所说的sql语句必须相同指的是这条语句加上参数最终拼接形成完整的sql文本必须完全一样。
也就是说在查询时并不会考虑sql语句本身的语义,如下代码所示。
<select id="getUserByNameInGender">
<if test="gender == 0">
SELECT * FROM user WHERE name=#{name}
</if>
<if test="gender == 1">
SELECT * FROM user WHERE 1=1 AND name=#{name}
</if>
</select>
我们知道
SELECT * FROM user WHERE 1=1 AND name=#{name}和SELECT * FROM user WHERE name=#{name}
其实是两个意义完全相同的sql语句,但mybatis并不会考虑这种情况。
总结
mybatis的一级缓存在什么时候产生?在什么情况下销毁?
产生:
在使用select标签进行查询时才会产生缓存,如果我们在诸如update标签中使用select语句进行查询并不会产生缓存。
销毁:
一级缓存是维持在同一个sqlsession中的,因此在对sqlsession使用close关闭或commit提交后,缓存就会被清空;
在使用sqlsession.rollback()进行回滚时,也会导致缓存被清空;
进行update操作也会造成缓存被清空,并且dll语句对查询缓存的影响并不局限于同一张表,就是说在A表进行查询产生缓存,然后对B表进行dll操作同样会使A表的select缓存失效(并且是所有缓存);
sqlsession还提供了clearCache()方法,这个方法可以让我们主动的对缓存进行清空;
经常有提到说mybatis一级缓存可能导致脏读的情况,是否真实?为什么?怎么解决?
针对mybatis一级缓存可能存在产生脏读的情况,我们可以模拟并发情况下的执行步骤来进行分析;
如果按照图中步骤来看,mybatis一级缓存的确存在可能造成脏读的情况;
所以在实际开发环境中应该避免使用mybatis缓存,而是使用类似redis这样的第三方插件进行全局缓存的管理;
补充
一级缓存的hashmap中key和value分别存的是?
key:hashcode值 + sqlId + sql语句;
value:映射结果对象;
后言
你好,很高兴认识你
本次关于 “ mybatis一级缓存 ” 的文章到此完结,若有疑问可以私信与我交流。
如果你也喜欢编程,如果你也喜欢敲代码,如果你也喜欢技术,欢迎联系~
我是
爱敲代码的小王bro