【Java 开发日记】我们来说一下 Mybatis 的缓存机制

【Java 开发日记】我们来说一下 Mybatis 的缓存机制

目录

核心概览

一级缓存

1. 作用域

2. 工作机制

3. 示例说明

4. 注意事项

二级缓存

1. 作用域

2. 开启与配置

3. 工作机制

4. 示例说明

5. 注意事项

缓存顺序与总结

使用建议


核心概览

  • 一级缓存:默认开启,作用范围在 同一个 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> <!-- 开启全局二级缓存,默认就是 true,但建议写明 --> <setting name="cacheEnabled" value="true"/> </settings> </configuration>

b. Mapper XML 文件
在需要开启二级缓存的 Mapper.xml 中添加 <cache/> 标签。

<?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"> <!-- 开启本 Mapper 的二级缓存 --> <cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/> <!-- 其他 SQL 定义 --> <select parameterType="long" resultType="User" useCache="true"> SELECT * FROM user WHERE id = #{id} </select> </mapper>
  • <cache/> 标签属性:
    • 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 级别的,有时会显得比较粗粒度。可以通过 <cache-ref> 让多个 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 等第三方缓存中间件来替代。
  • 理解缓存机制有助于解决一些“诡异”的问题,比如在同一个事务中,先后查询和更新,但由于一级缓存的存在,后续查询可能看不到其他线程的更新。

如果小假的内容对你有帮助,请点赞评论收藏。创作不易,大家的支持就是我坚持下去的动力!

Read more

DeOldify图像上色效果展示:黑白医疗影像→组织结构色彩增强显示

DeOldify图像上色效果展示:黑白医疗影像→组织结构色彩增强显示 1. 项目简介与核心价值 医疗影像分析是医学诊断中的重要环节,但很多历史医疗影像资料都是黑白的,这给医生的诊断和分析带来了一定困难。传统的黑白影像缺乏色彩信息,很多细微的组织结构和病理变化难以直观观察。 基于DeOldify深度学习模型的黑白图像上色技术,为医疗影像分析带来了全新的可能性。这个技术能够智能地为黑白医疗影像添加逼真的色彩,让组织结构更加清晰可见,帮助医生更好地识别和分析影像中的细节信息。 1.1 技术原理简述 DeOldify采用先进的U-Net深度学习架构,结合ResNet编码器,通过大量医疗影像数据训练而成。模型能够理解不同组织结构的色彩特征,为黑白影像智能添加符合医学标准的色彩表现。 核心优势: * 智能色彩还原:基于深度学习自动识别组织类型并添加相应色彩 * 细节保持:在色彩化的同时保持影像的细节清晰度 * 医学准确性:色彩表现符合医学影像标准 * 批量处理:支持大量医疗影像的快速处理 2. 医疗影像上色效果展示 2.1 组织切片影像上色效果 案例一:病理切片色彩

By Ne0inhk
2025年最新vscode无法下载.vsix插件(官网marketplace找不到DownLoad Extension下载.vsix插件按钮)的解决方案【以C/C++插件1.23.5之后的版本为例】

2025年最新vscode无法下载.vsix插件(官网marketplace找不到DownLoad Extension下载.vsix插件按钮)的解决方案【以C/C++插件1.23.5之后的版本为例】

目录 一、背景 二、解决方法1 三、解决方法2 1、访问C/C++插件的github仓库地址 2、选择对应版本的Asserts 3、下载相应版本的.vsix文件 4、vscode安装插件“Install from VSIX” 四、总结 一、背景 最近因为公司安全要求,内网开发需要严格隔离,无法访问公共互联网,在安装完vscode后,代码库使用的是C/C++语言,无法进行变量、函数的声明、实现的跳转,使用起来特别麻烦,因此安装C/C++插件能够更方便进行代码阅读。而因内网隔离,vscode基本的插件无法通过在线联网的方式安装,只能通过安装.vsix文件进行安装。 之前下载.vsix文件都是通过访问官网扩展市场 Extensions for Visual Studio family

By Ne0inhk
C++贪吃蛇游戏代码学习笔记(附完整实现解析)

C++贪吃蛇游戏代码学习笔记(附完整实现解析)

C++贪吃蛇游戏代码学习笔记(附完整实现解析) 一、游戏核心概述 1.1 游戏原理 贪吃蛇游戏是经典的即时交互游戏,核心逻辑为:通过键盘控制蛇的移动方向,蛇在固定地图内追逐食物,吃到食物后身体变长、分数增加且移动速度加快,若蛇头触碰边界或自身身体则游戏结束。 本实现基于C++控制台开发,借助Windows API控制光标位置与隐藏,通过时间戳控制蛇的移动频率,采用结构化设计封装地图、蛇、坐标等核心要素,逻辑清晰且易扩展。 1.2 核心依赖库 库名核心用途iostream控制台输入输出(绘制地图、显示分数等)windows.h控制控制台光标(隐藏、移动)、获取标准输出句柄chrono时间戳计算,控制蛇的移动频率(毫秒级精度)conio.h检测键盘输入(_kbhit())、获取按键(_getch())ctime/cstdlib生成随机数种子(srand()),用于食物随机生成sstream/cstring分数转换为字符串(实时更新分数显示) 二、核心数据结构设计

By Ne0inhk