我们来说一下 Mybatis 的缓存机制

2025-12-04 0 233

核心概览

  • 一级缓存:默认开启,作用范围在 同一个 SqlSession 内。
  • 二级缓存:需要手动配置开启,作用范围在 同一个 Mapper 命名空间(即同一个 Mapper 接口)内,可以被多个 SqlSession 共享。

一级缓存

1. 作用域

  • SqlSession 级别:当同一个 SqlSession 执行相同的 SQL 查询时,MyBatis 会优先从缓存中获取数据,而不是直接查询数据库。
  • 它是 默认开启 的,无法关闭,但可以配置其作用范围(SESSIONSTATEMENT)。

2. 工作机制

  1. 第一次执行查询后,查询结果会被存储到 SqlSession 关联的一级缓存中。
  2. 在同一个 SqlSession 中,再次执行 完全相同的 SQL 查询(包括语句和参数)时,会直接返回缓存中的对象,而不会去数据库查询。
  3. 如果 SqlSession 执行了 增(INSERT)、删(DELETE)、改(UPDATE) 操作,或者调用了 commit()close()rollback() 方法,该 SqlSession 的一级缓存会被清空。这是为了防止读取到脏数据。

3. 示例说明

// 假设获取的 SqlSession 和 UserMapper
try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);

    // 第一次查询,会发送 SQL 到数据库
    User user1 = mapper.selectUserById(1L);
    System.out.println(user1);

    // 第二次查询,SQL 和参数完全相同,直接从一级缓存返回,不查询数据库
    User user2 = mapper.selectUserById(1L);
    System.out.println(user2);

    // 判断是否为同一个对象(是,因为从缓存中返回的是同一个对象的引用)
    System.out.println(user1 == user2); // 输出:true

    // 执行一个更新操作
    mapper.updateUser(user1);
    // 此时,一级缓存被清空

    // 第三次查询,因为缓存被清空,会再次发送 SQL 到数据库
    User user3 = mapper.selectUserById(1L);
    System.out.println(user3 == user1); // 输出:false (虽然是同一条数据,但已是新对象)
}

4. 注意事项

  • 对象相同:一级缓存返回的是 同一个对象的引用,因此在同一个 SqlSession 内,你操作的都是同一个 Java 对象。
  • 分布式环境:一级缓存无法在多个应用服务器之间共享,因为它绑定在单个请求的 SqlSession 上。

二级缓存

1. 作用域

  • Mapper 级别 / Namespace 级别:多个 SqlSession 在访问同一个 Mapper 的查询时,可以共享其缓存。
  • 它是 默认关闭 的,需要在全局配置中开启,并在具体的 Mapper XML 中显式配置。

2. 开启与配置

a. 全局配置文件 ( mybatis-config.xml )
必须显式设置开启二级缓存(虽然默认是 true,但显式声明是个好习惯)。

<configuration>
<settings>

<setting name=\"cacheEnabled\" value=\"true\"/>
</settings>
</configuration>

b. Mapper XML 文件
在需要开启二级缓存的 Mapper.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=\"com.example.mapper.UserMapper\">

<cache
eviction=\"FIFO\"
flushInterval=\"60000\"
size=\"512\"
readOnly=\"true\"/>


<select id=\"selectUserById\" parameterType=\"long\" resultType=\"User\" useCache=\"true\">
SELECT * FROM user WHERE id = #{id}
</select>
</mapper>

  •  标签属性:

    • eviction:缓存回收策略。

      • LRU(默认):最近最少使用。
      • FIFO:先进先出。
      • SOFT:软引用,基于垃圾回收器状态和软引用规则移除。
      • WEAK:弱引用,更积极地移除。
    • flushInterval:缓存刷新间隔(毫秒),默认不清空。

    • size:缓存存放多少元素。

    • readOnly:是否为只读。

      • true:返回相同的缓存对象实例,性能好,但不允许修改。
      • false(默认):通过序列化返回缓存对象的拷贝,安全,性能稍差。

3. 工作机制

  1. 当一个 SqlSession 执行查询后,在关闭或提交时,其查询结果会被存入二级缓存。
  2. 另一个 SqlSession 执行相同的查询时,会先从二级缓存中查找数据。如果找到,则直接返回,否则再去数据库查询。
  3. 任何一个 SqlSession 执行了 增、删、改 操作并 commit() 后,会清空 整个对应 Mapper 的二级缓存,以保证数据一致性。

4. 示例说明

// 第一个 SqlSession
try (SqlSession sqlSession1 = sqlSessionFactory.openSession()) {
    UserMapper mapper1 = sqlSession1.getMapper(UserMapper.class);
    User user1 = mapper1.selectUserById(1L); // 查询数据库
    sqlSession1.close(); // 关闭时,数据存入二级缓存
}

// 第二个 SqlSession(与第一个不同)
try (SqlSession sqlSession2 = sqlSessionFactory.openSession()) {
    UserMapper mapper2 = sqlSession2.getMapper(UserMapper.class);
    // 查询相同的 SQL,直接从二级缓存获取,不查询数据库
    User user2 = mapper2.selectUserById(1L);
}

// 第三个 SqlSession,执行了更新
try (SqlSession sqlSession3 = sqlSessionFactory.openSession()) {
    UserMapper mapper3 = sqlSession3.getMapper(UserMapper.class);
    User user = mapper3.selectUserById(1L);
    user.setName(\"New Name\");
    mapper3.updateUser(user); // 执行更新
    sqlSession3.commit(); // 提交时,清空 UserMapper 的二级缓存
}

// 第四个 SqlSession
try (SqlSession sqlSession4 = sqlSessionFactory.openSession()) {
    UserMapper mapper4 = sqlSession4.getMapper(UserMapper.class);
    // 因为缓存已被清空,所以会再次查询数据库
    User user4 = mapper4.selectUserById(1L);
}

5. 注意事项

  • 实体类序列化:如果二级缓存的 readOnly=\"false\",那么对应的实体类必须实现 Serializable 接口。
  • 事务提交:只有在 SqlSession 执行 commit()close() 时,数据才会从一级缓存转存到二级缓存。
  • 缓存粒度:二级缓存是 Mapper 级别的,有时会显得比较粗粒度。可以通过 让多个 Mapper 共享一个缓存,但不推荐,容易引起数据混乱。

缓存顺序与总结

当发起一个查询请求时,MyBatis 的缓存查询顺序是:

  1. 先查二级缓存:查看当前 Mapper 的二级缓存中是否有数据。
  2. 再查一级缓存:如果二级缓存没有,再查看当前 SqlSession 的一级缓存中是否有数据。
  3. 最后查数据库:如果两级缓存都没有,才发送 SQL 语句到数据库执行查询。

查询到的数据会 先存入一级缓存,在 SqlSession 关闭或提交时,再转存到二级缓存

特性 一级缓存 二级缓存
作用域 SqlSession Mapper (Namespace)
默认状态 开启 关闭
是否共享 否,Session 独享 是,跨 Session 共享
清空时机 UPDATE/INSERT/DELETE, commit(), close() 同 Mapper 的 UPDATE/INSERT/DELETE + commit()

使用建议

  • 查询多,修改少的数据适合使用二级缓存,如字典表、配置项。
  • 数据实时性要求高的场景(如交易、订单)应谨慎使用二级缓存,或者设置较短的刷新间隔。
  • 在分布式环境中,默认的二级缓存(基于内存)是无法共享的,需要集成 Redis、Ehcache 等第三方缓存中间件来替代。
  • 理解缓存机制有助于解决一些“诡异”的问题,比如在同一个事务中,先后查询和更新,但由于一级缓存的存在,后续查询可能看不到其他线程的更新。

收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

申明:本文由第三方发布,内容仅代表作者观点,与本网站无关。对本文以及其中全部或者部分内容的真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。本网发布或转载文章出于传递更多信息之目的,并不意味着赞同其观点或证实其描述,也不代表本网对其真实性负责。

左子网 开发教程 我们来说一下 Mybatis 的缓存机制 https://www.zuozi.net/3581.html

常见问题
  • 1、自动:拍下后,点击(下载)链接即可下载;2、手动:拍下后,联系卖家发放即可或者联系官方找开发者发货。
查看详情
  • 1、源码默认交易周期:手动发货商品为1-3天,并且用户付款金额将会进入平台担保直到交易完成或者3-7天即可发放,如遇纠纷无限期延长收款金额直至纠纷解决或者退款!;
查看详情
  • 1、描述:源码描述(含标题)与实际源码不一致的(例:货不对板); 2、演示:有演示站时,与实际源码小于95%一致的(但描述中有”不保证完全一样、有变化的可能性”类似显著声明的除外); 3、发货:不发货可无理由退款; 4、安装:免费提供安装服务的源码但卖家不履行的; 5、收费:价格虚标,额外收取其他费用的(但描述中有显著声明或双方交易前有商定的除外); 6、其他:如质量方面的硬性常规问题BUG等。 注:经核实符合上述任一,均支持退款,但卖家予以积极解决问题则除外。
查看详情
  • 1、左子会对双方交易的过程及交易商品的快照进行永久存档,以确保交易的真实、有效、安全! 2、左子无法对如“永久包更新”、“永久技术支持”等类似交易之后的商家承诺做担保,请买家自行鉴别; 3、在源码同时有网站演示与图片演示,且站演与图演不一致时,默认按图演作为纠纷评判依据(特别声明或有商定除外); 4、在没有”无任何正当退款依据”的前提下,商品写有”一旦售出,概不支持退款”等类似的声明,视为无效声明; 5、在未拍下前,双方在QQ上所商定的交易内容,亦可成为纠纷评判依据(商定与描述冲突时,商定为准); 6、因聊天记录可作为纠纷评判依据,故双方联系时,只与对方在左子上所留的QQ、手机号沟通,以防对方不承认自我承诺。 7、虽然交易产生纠纷的几率很小,但一定要保留如聊天记录、手机短信等这样的重要信息,以防产生纠纷时便于左子介入快速处理。
查看详情

相关文章

猜你喜欢
发表评论
暂无评论
官方客服团队

为您解决烦忧 - 24小时在线 专业服务