#Mybatis 关于mybatis的一级缓存

本文介绍了Mybatis的一级缓存机制,包括其工作原理、命中原则和应用场景。一级缓存默认开启,基于session,作用域仅限session内。命中原则涉及statementId、查询参数、分页参数和SQL语句的匹配。一级缓存会在session关闭或提交、回滚、更新操作后被清空。在并发环境下可能导致脏读,实际开发中建议配合全局缓存管理工具如Redis使用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

# 本篇文章主要是为了帮助自己总结和加深理解,若能帮助到其他小伙伴也是极好的

基本介绍

        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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值