
文章目录
第9章:MyBatis多级缓存和懒加载
一级缓存
什么是缓存?

一级缓存核心定位
- 一级缓存是 MyBatis 内置的 默认缓存机制,无需手动配置,默认开启。
- 作用域:
- 仅限当前
SqlSession(数据库会话) - 不同 SqlSession 之间的缓存相互隔离。
- 仅限当前
- 缓存介质:
- 内存(基于
HashMap存储),缓存键由Mapper ID + SQL 语句 + 参数 + 分页信息 + 环境信息组成。
核心价值:
-
减少同一 SqlSession 内重复查询的数据库交互,提升查询性能。
-
一级缓存生效与失效条件
场景类型 生效条件 失效条件 核心前提 同一 SqlSession、相同 Mapper ID+SQL + 参数 不同 SqlSession、SQL / 参数 / 分页不同 数据库操作影响 未执行增删改(insert/update/delete)操作 执行增删改操作(自动清空当前 SqlSession 缓存) 手动干预 未调用 clearCache()或close()方法调用 sqlSession.clearCache()(手动清空)、sqlSession.close()(关闭会话)配置影响 默认配置(无特殊禁用) 全局配置 localCacheScope=STATEMENT(禁用一级缓存)
一级缓存工作流程

一级缓存核心特性:

java
@Service
@Slf4j
public class UserService {
@Autowired
private SqlSessionTemplate sqlSessionTemplate;
/*一级缓存 有效的访问*/
public void firstCache(){
SqlSessionFactory sqlSessionFactory = sqlSessionTemplate.getSqlSessionFactory();
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
User user = userMapper.selectById(1);
log.info("第一次查询结果:{}",user);
User user2 = userMapper.selectById(1);
log.info("第二次查询结果:{}",user2);
log.info("两个user是否是同一个对象:{}",user==user2);
sqlSession.close();
}
/*一级缓存 失效的访问*/
public void firstCacheInvalidation(){
SqlSessionFactory sqlSessionFactory = sqlSessionTemplate.getSqlSessionFactory();
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
User user = userMapper.selectById(1);
log.info("第一次查询结果:{}",user);
/*执行 写的操作*/
user.setAge(18);
int rows = userMapper.update(user);
log.info("执行写的操作,影响行数:{}",rows);
//清空缓存
// sqlSession.clearCache();
User user2 = userMapper.selectById(1);
log.info("第二次查询结果:{}",user2);
log.info("两个user是否是同一个对象:{}",user==user2);
sqlSession.close();
}
}
二级缓存怎么使用
核心概念界定
二级缓存核心定位
- 二级缓存(Mapper 级缓存)是 MyBatis 的 跨 SqlSession 缓存
- 作用域为同一个 Mapper(namespace)
- 不同 SqlSession 可共享缓存数据。
- 缓存介质:
- 默认内存(HashMap),支持自定义(如 Redis等第三方缓存)
- 核心价值:
- 减少不同 SqlSession 间重复查询的数据库交互
- 适用于查询频率高、修改频率低的数据(如字典表、配置表)
- 依赖条件:实体类需实现
Serializable接口,需手动开启
一级缓存与二级缓存核心区别
| 对比维度 | 一级缓存(SqlSession 级) | 二级缓存(Mapper 级) |
|---|---|---|
| 作用域 | 单个 SqlSession | 同一个 Mapper(namespace) |
| 共享性 | 不可跨 SqlSession 共享 | 可跨 SqlSession 共享 |
| 开启方式 | 默认开启,无需配置 | 需全局配置 + Mapper 配置手动开启 |
| 实体类要求 | 无强制序列化要求 | 必须实现 Serializable 接口 |
| 失效触发 | 同一 SqlSession 内增删改、clearCache () | 对应 Mapper 内增删改操作、缓存过期等 |
| 适用场景 | 单会话内重复查询 | 多会话共享高频查询数据 |
二级缓存工作流程

二级缓存开启条件(三步缺一不可)
- 全局配置开启:
- springBoot 配置文件中设置
cacheEnabled=true
- springBoot 配置文件中设置
- Mapper 级开启:
- 在 Mapper XML 中添加
<cache/>标签
- 在 Mapper XML 中添加
- 实体类序列化:
- 缓存的实体类必须实现
java.io.Serializable接口(避免序列化异常)。
- 缓存的实体类必须实现
二级缓存配置详解
yaml
mybatis:
configuration:
cache-enabled: true # 启用二级缓存
xml
<!-- Mapper XML 中配置二级缓存 -->
<mapper namespace="com.example.mapper.UserMapper">
<!-- 开启二级缓存配置 -->
<cache
eviction="LRU"
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 | LRU | 缓存回收策略 |
flushInterval |
毫秒数 | 无 | 缓存刷新间隔 |
size |
正整数 | 1024 | 缓存引用数量 |
readOnly |
true/false | false | 是否只读 |
blocking |
true/false | false | 是否使用阻塞缓存 |
常见的缓存回收策略:
| 策略缩写 | 全称 | 策略说明 |
|---|---|---|
| LRU | Least Recently Used | 移除最长时间未被使用的对象(默认策略)。 |
| FIFO | First In First Out | 按对象进入缓存的顺序来移除它们。 |
| SOFT | Soft Reference | 基于垃圾回收器状态和软引用规则来移除对象。 |
| WEAK | Weak Reference | 更积极地基于垃圾收集器状态和弱引用规则移除对象。 |
java
// 使用二级缓存需要实体类实现Serializable接口
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User implements Serializable {
private Long id;
private String username;
private String email;
private Integer age;
private Date createTime;
private Date updateTime;
// 关联对象也需要序列化
}
java
// 二级缓存服务演示
@Service
@Slf4j
public class SecondLevelCacheService {
@Autowired
private UserMapper userMapper;
/**
* 演示二级缓存跨SqlSession共享
*/
public void demonstrateSecondLevelCache() {
User user1 = userMapper.selectUserById(1L);
log.info("SqlSession查询: {}", user1);
// 重新调用方法模拟第二个SqlSession(实际应用中可能是另一个请求)
log.info("=== 二级缓存演示结束 ===");
}
/**
* 二级缓存失效场景
*/
public void demonstrateSecondLevelCacheInvalidation() {
// 执行更新操作
User user1 = new User();
user1.setId(1L);
user1.setEmail("newemail@example.com");
int updateCount = userMapper.updateUser(user1);
log.info("更新影响行数: {}", updateCount);
}
}
懒加载
懒加载核心定位
- 懒加载(延迟加载)是 MyBatis 关联查询的 性能优化机制
- 指查询主对象时,不立即加载关联对象
- 而是在首次访问关联对象时才触发查询xa
- 对立概念:
- 立即加载(默认行为),查询主对象时同时加载所有关联对象
- 核心价值:
- 避免不必要的关联查询,
- 减少数据库压力
- 如仅需查询用户基本信息时,无需加载其所有订单
- 适用场景:
- 一对一、一对多、多对多关联查询,且关联数据不总是需要使用
懒加载与立即加载对比

懒加载开启条件
- 全局配置开启延迟加载:
lazyLoadingEnabled=true(默认 false)
- 关闭积极加载:
aggressiveLazyLoading=false- SpringBoot 2.x+ 已默认关闭,确保按需加载
- 关联标签配置(可选):
association或collection标签添加fetchType="lazy"(优先级高于全局配置)
懒加载配置详解
yaml
mybatis:
configuration:
lazy-loading-enabled: true # 开启全局懒加载
aggressive-lazy-loading: false # 关闭积极加载(按需加载)
map-underscore-to-camel-case: true
懒加载实战案例
xml
<!-- 基础用户结果映射(不包含关联对象) -->
<resultMap id="BaseUserMap" type="com.example.entity.User">
<id column="user_id" property="userId"/>
<result column="user_name" property="userName"/>
<result column="card_id" property="cardId"/>
</resultMap>
<!-- 嵌套查询结果映射:使用子查询方式加载关联对象 -->
<resultMap id="UserWithIdCardNestedQueryMap" type="com.example.entity.User" extends="BaseUserMap">
<association property="idCard"
column="card_id"
select="com.example.mapper.IdCardMapper.selectById"
javaType="com.example.entity.IdCard"
fetchType="lazy"
/>
</resultMap>
<!-- 嵌套查询方式:查询用户(触发子查询加载身份证) -->
<select id="selectUserWithIdCardNested" resultMap="UserWithIdCardNestedQueryMap">
SELECT
user_id,
user_name,
card_id
FROM t_user
WHERE user_id = #{userId}
</select>
懒加载最佳实践
-
适用场景:
- 关联数据量大,且不经常使用
- 需要快速响应的列表查询
- 移动端应用,减少数据传输
-
注意事项:
) -->
SELECT
user_id,
user_name,
card_id
FROM t_user
WHERE user_id = #{userId}
懒加载最佳实践
-
适用场景:
- 关联数据量大,且不经常使用
- 需要快速响应的列表查询
- 移动端应用,减少数据传输
-
注意事项:
- 注意 N+1 查询问题
-