MyBatis的一级缓存和二级缓存
- [1. 一级缓存](#1. 一级缓存)
-
- [1.1 是什么](#1.1 是什么)
- [1.2 缓存命中的条件](#1.2 缓存命中的条件)
- [1.3 我的测试](#1.3 我的测试)
- [2. 二级缓存](#2. 二级缓存)
1. 一级缓存
1.1 是什么
🖊 MyBatis 一级缓存也叫「本地缓存」,是 SqlSession 级别的缓存(每个 SqlSession 有自己专属的一级缓存),默认自动开启,无需任何配置。
🖊 可以把它理解为:每个 SqlSession 都有一个 "专属口袋🎒",第一次查数据库拿到的数据会放进这个口袋,同一个 SqlSession 再查相同数据时,直接从口袋里拿,不用再问数据库要。

✏ 同一个 SqlSession 执行第一次查询:缓存中无数据 → 查数据库 → 结果存入一级缓存 → 返回数据
✏ 同一个 SqlSession 执行第二次相同查询:缓存中有数据 → 直接返回缓存数据(不查库)
✏ 当 SqlSession 执行增删改、提交(commit)、回滚(rollback)、关闭(close)时,一级缓存会被自动清空(避免缓存数据和数据库不一致)
1.2 缓存命中的条件
- 一级缓存不是 "随便查都能用",必须满足以下所有条件,才会命中缓存:
🖊 SqlSession 相同: 不同 SqlSession 的缓存相互隔离(比如 SqlSession1 和 SqlSession2 各有自己的缓存,互不共享)
🖊 查询条件完全相同: SQL 语句、参数、分页 / 排序条件、行锁等完全一致(比如 select * from user where id=1 和 select * from user where id=2 是不同的缓存键)
🖊 Mapper 标识相同: 即 Mapper 的 namespace + SQL 的 id 相同(比如 com.user.mapper.UserMapper.selectById 这个唯一标识)
🖊 未执行清空缓存的操作: 比如增删改、commit、rollback 等
1.3 我的测试
java
package com.jagochan.train.business.controller;
import com.jagochan.train.business.resp.TrainQueryResp;
import com.jagochan.train.business.service.TrainService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@Slf4j
@Tag(name = "测试控制器", description = "用于测试的控制器")
@RestController
@RequestMapping("/business/test")
public class SuccessController {
@Resource
private TrainService trainService;
@Resource
private TransactionTemplate transactionTemplate;
@Operation(description = "测试MyBatis的一级缓存")
@GetMapping("/testMyBatisLevelOneCache/{withTransaction}")
public Object testMyBatisLevelOneCache(@PathVariable Boolean withTransaction) {
if (withTransaction) {
return transactionTemplate.execute(new TransactionCallback() { // 有事务
@Override
public Object doInTransaction(TransactionStatus status) {
try {
log.debug("😀第1次查询 with transaction");
List<TrainQueryResp> res1 = trainService.listAll();
log.debug("😀第2次查询 with transaction");
List<TrainQueryResp> res2 = trainService.listAll();
log.debug("😀finish with transaction");
return "res1💴" + res1;
} catch (Exception e) {
status.setRollbackOnly();
return false;
}
}
});
} else {
log.debug("😀第1次查询 without transaction");
List<TrainQueryResp> res1 = trainService.listAll();
log.debug("😀第2次查询 without transaction");
List<TrainQueryResp> res2 = trainService.listAll();
log.debug("😀finish without transaction");
return "res2🍀" + res2;
}
}
}
🖊 当没有事务的时候:每一次查询都会查询数据库

🖊 当有 事务的时候:第二次查询缓存命中

❓ 关闭一级缓存
🖊 在配置文件中增加如下配置
yml
mybatis:
configuration:
local-cache-scope: statement # 默认是session
2. 二级缓存
✏ MyBatis 二级缓存是 Mapper/Namespace 级 全局缓存,默认关闭,需手动配置
✏ 开启条件:全局 ① cacheEnabled=true;② Mapper 加 <cache/>;③ 实体类序列化
✏ 数据同步:SqlSession 提交 / 关闭时,将一级缓存数据同步到二级缓存
✏ 核心特点:多 SqlSession 共享,增删改自动清空,默认内存存储
✏ 适用场景:单机 + 只读 / 少写数据
