mybatis的一级缓存和事务

探讨MyBatis中一级缓存的工作原理,包括在不同事务条件下如何影响查询效率,及如何利用缓存提高性能。

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

Mybatis 默认开启一级缓存,其一级缓存是SqlSession级别的,sqlSession级别的缓存,意味着伴随着sqlSession的生死。

一级缓存的作用:当使用同一个sqlSession对数据库做相同的查询时,第一次查询的结果会放入缓存,在缓存中是以Map的形式存放的,当后面相同的查询到来时就会去缓存中取数据,而不再查询数据库。

注意:这里的后面相同的查询到来时就会去缓存中取数据是有条件的,条件就是在第一次查询后和后几次的相同查询之间没有增删改操作,否则缓存中的数据会被清除,后来的相同查询会因在缓存中找不到缓存结果而去数据库中查询。

mybatis认为:对于两次查询,如果以下条件都完全一样,那么就认为它们是完全相同的两次查询。

1. 传入的statementId。

2. 查询时要求的结果集中的结果范围。

3. 这次查询所产生的最终要传递给JDBC java.sql.Preparedstatement的Sql语句字符串(boundSql.getSql() )。

4. 传递给java.sql.Statement要设置的参数值。

 测试一下不加事务时,两次相同的查询一级缓存的情况:

mapper

<select id="getItem" parameterType="java.lang.Long" resultType="com.lz.springboot.pojo.Item">
        select * from tb_item WHERE id = #{id}
</select>

service 

 public Item getItem(long id){
        itemMapper.getItem(id);
        return itemMapper.getItem(id);
    }

日志打印(截取了一部分):

可以明显的看出是对数据库操作了两次,也就是说mybatis的一级缓存没有起到作用,是什么原因呢,从标记处可以看到在第一次查询时create一个SqlSession,然后从数据库连接池中fetch jdbc的连接,然后去数据库中查询,紧接着一句closing no transational SqlSession 这里就是问题的所在也就是说每一次查询完数据库后,SqlSession被关闭,上面说过一级缓存是SqlSession级别的缓存,一级缓存伴随着SqlSession的关闭,也就失效了,第二次相同的查询到来时会重复上面的步骤。也可以看出在不加事务时,mybatis会认为每一次对数据库的查询都是一次回话,多次就是多个回话,多个SqlSession。

加事务时,两次相同的查询一级缓存的情况:

此时service

@Transactional
    public Item getItem(long id){
        itemMapper.getItem(id);
        return itemMapper.getItem(id);
    }

日志打印:

与不加事务时的日志有所不同,可以看出只对数据库操作了一次。一开始也是新建一个SqlSession,接下来一步是为sqlsession注册事务同步,然后再去查询数据库,然后接下来是最大不同,mybatis并没有将当前的sqlsession关闭,而是Releasing transactional SqlSession,减少SqlSession的引用次数(每次对数据库的操作都是使用当前SqlSession的引用,这里的减少次数就是使用完之后将此次的引用销毁),当第二次相同的查询的时候,从当前的事务中fetch SqlSession,然后去缓存中取数据,减少SqlSession的引用次数,然后提交事务,最后关闭SqlSession。在这里加上了事务后,两次的相同查询会被当做一次回话,两次查询也就会使用同一个SqlSession,缓存也就有效。

事务中,在两次相同查询之间有增删改操作时的情况 

 在原来的基础上添加更新操作

mapper 添加

<update id="updateById" parameterType="java.lang.Long">
        update tb_item SET price=29900000 WHERE id=#{id}
    </update>

service 添加

@Transactional
    public Item getItem(long id){
        itemMapper.getItem(id);
        itemMapper.updateById(id);
        return itemMapper.getItem(id);
    }

日志打印:

可以就算是有事务,相同的查询也会对数据库操作两次,原因就是在两次相同的查询之间有更新操作,会导致SqlSession缓存中的数据被清空,当第二次查询到来时,缓存中没有缓存数据,所以又去查询了数据库。

此次和不加事务缓存失效是两种不同的情况:不加事务时是因为两次查询不是同一个SqlSession,更直接的说就是不是同一个缓存。这次是同一个缓存,但是中间的增删改操作到时缓存数据失效被清空。

 

由于没有能力分析源码,所以不能从源码的角度分析为什么这样。

个人认为在开发中即使是查询service也应该加入事务,尤其是对于一个service中存在多个查询操作,优点是当存在着相同的查询并且两次查询之间不存在增删改操作时,可以使用到缓存,即使是不相同的查询,也会减少SqlSession实例的创建和销毁,能够提升效率,减少时间的浪费。

 

 

 

 

 

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值