全部学科
NodeJS全栈
nodejs
Python全栈
python
小程序首页
📅 2026-05-20 6 分钟 ✍️ juanwangdev

一级缓存机制

MyBatis 一级缓存是 SqlSession 级别的本地缓存,默认开启。在同一 SqlSession 中执行相同查询时,第二次查询会直接从缓存返回结果,不再访问数据库。

一级缓存原理

一级缓存基于 PerpetualCache 实现,底层使用 HashMap 存储数据。每个 SqlSession 持有独立的缓存实例,不同 SqlSession 之间缓存不共享。

Java
// MyBatis 内部结构
public class BaseExecutor implements Executor {
    // 一级缓存实例
    protected PerpetualCache localCache;
    
    @Override
    public <E> List<E> query(MappedStatement ms, Object parameter, 
                             RowBounds rowBounds, ResultHandler resultHandler) {
        // 生成缓存 Key
        CacheKey key = createCacheKey(ms, parameter, rowBounds);
        // 先从本地缓存获取
        List<E> list = (List<E>) localCache.getObject(key);
        if (list != null) {
            return list; // 缓存命中,直接返回
        }
        // 缓存未命中,查询数据库
        list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key);
        return list;
    }
}

缓存 Key 生成规则

缓存 Key 由以下因素共同决定:

因素说明
MappedStatement.idSQL 语句的唯一标识
RowBounds分页偏移量
SQL 语句本身包含参数的 SQL 文本
参数值传入的实际参数值

只有上述因素完全一致时,才会命中缓存。

缓存命中条件

要命中一级缓存,必须同时满足以下条件:

  1. 同一个 SqlSession:不同 Session 缓存隔离
  2. 相同的 SQL 语句MappedStatement.id 一致
  3. 相同的参数值:参数值完全相等
  4. 相同的 RowBounds:分页条件一致
  5. 中间没有执行写操作:INSERT/UPDATE/DELETE 会清空缓存
  6. 没有手动调用 clearCache():主动清理缓存

命中示例

Java
SqlSession session = sqlSessionFactory.openSession();
UserMapper mapper = session.getMapper(UserMapper.class);

// 第一次查询,访问数据库
User user1 = mapper.selectById(1);
System.out.println("Query 1: " + user1);

// 第二次相同查询,命中缓存,不访问数据库
User user2 = mapper.selectById(1);
System.out.println("Query 2: " + user2);

// 两次查询返回同一对象引用
System.out.println(user1 == user2); // true

session.close();

不命中示例

Java
SqlSession session1 = sqlSessionFactory.openSession();
SqlSession session2 = sqlSessionFactory.openSession();

UserMapper mapper1 = session1.getMapper(UserMapper.class);
UserMapper mapper2 = session2.getMapper(UserMapper.class);

// 不同 SqlSession,缓存不共享
User user1 = mapper1.selectById(1); // 访问数据库
User user2 = mapper2.selectById(1); // 访问数据库

System.out.println(user1 == user2); // false

session1.close();
session2.close();

缓存失效场景

一级缓会在以下场景中自动清空:

1. 执行写操作后自动清空

任何 INSERT/UPDATE/DELETE 操作都会清空一级缓存:

Java
SqlSession session = sqlSessionFactory.openSession();
UserMapper mapper = session.getMapper(UserMapper.class);

// 第一次查询,访问数据库
User user = mapper.selectById(1);

// 执行更新操作,清空一级缓存
mapper.updateUser(user);
session.commit();

// 再次查询,缓存已失效,重新访问数据库
User user2 = mapper.selectById(1);
System.out.println(user == user2); // false
写操作类型清空缓存说明
INSERT新增数据后缓存失效
UPDATE更新数据后缓存失效
DELETE删除数据后缓存失效
COMMIT提交事务时清空缓存

2. 手动清空缓存

Java
SqlSession session = sqlSessionFactory.openSession();
UserMapper mapper = session.getMapper(UserMapper.class);

// 查询数据
User user = mapper.selectById(1);

// 手动清空一级缓存
session.clearCache();

// 再次查询,重新访问数据库
User user2 = mapper.selectById(1);
System.out.println(user == user2); // false

3. 事务回滚

Java
SqlSession session = sqlSessionFactory.openSession();
UserMapper mapper = session.getMapper(UserMapper.class);

try {
    User user = mapper.selectById(1);
    
    // 执行更新
    mapper.updateUser(user);
    
    // 模拟异常
    if (true) throw new RuntimeException("test");
    
    session.commit();
} catch (Exception e) {
    // 回滚事务,清空一级缓存
    session.rollback();
}

// 回滚后缓存已失效
User user2 = mapper.selectById(1);

4. Session 关闭

Java
SqlSession session = sqlSessionFactory.openSession();
UserMapper mapper = session.getMapper(UserMapper.class);

User user = mapper.selectById(1);

// 关闭 Session,一级缓存被销毁
session.close();

// 新 Session,全新缓存
SqlSession newSession = sqlSessionFactory.openSession();
UserMapper newMapper = newSession.getMapper(UserMapper.class);
User user2 = newMapper.selectById(1);

localCacheScope 配置

MyBatis 提供 localCacheScope 配置项,控制一级缓存的作用域:

配置值说明适用场景
SESSION默认值,Session 级别缓存一般业务场景
STATEMENT语句级别,每次查询后清空需要实时数据的场景

SESSION 级别(默认)

XML
<!-- mybatis-config.xml -->
<settings>
    <!-- 默认值为 SESSION,可省略 -->
    <setting name="localCacheScope" value="SESSION"/>
</settings>

SESSION 级别下,同一 SqlSession 内多次相同查询命中缓存:

Java
SqlSession session = sqlSessionFactory.openSession();
UserMapper mapper = session.getMapper(UserMapper.class);

// 查询 3 次
User u1 = mapper.selectById(1); // 访问数据库
User u2 = mapper.selectById(1); // 命中缓存
User u3 = mapper.selectById(1); // 命中缓存

STATEMENT 级别

XML
<settings>
    <setting name="localCacheScope" value="STATEMENT"/>
</settings>

STATEMENT 级别下,每次查询后自动清空缓存:

Java
SqlSession session = sqlSessionFactory.openSession();
UserMapper mapper = session.getMapper(UserMapper.class);

// 查询 3 次
User u1 = mapper.selectById(1); // 访问数据库
User u2 = mapper.selectById(1); // 访问数据库(STATEMENT 模式下已清空)
User u3 = mapper.selectById(1); // 访问数据库

STATEMENT 级别适用于对数据实时性要求高的场景,如金融交易、库存管理等。

缓存与事务的关系

一级缓存的生命周期与事务紧密相关:

Java
SqlSession session = sqlSessionFactory.openSession();
try {
    UserMapper mapper = session.getMapper(UserMapper.class);
    
    // 第一次查询
    User user = mapper.selectById(1);
    
    // 更新操作
    user.setUsername("newName");
    mapper.updateUser(user);
    
    // 提交事务,同时清空一级缓存
    session.commit();
    
    // 查询需要重新访问数据库
    User user2 = mapper.selectById(1);
    
} catch (Exception e) {
    // 回滚事务,同时清空一级缓存
    session.rollback();
} finally {
    session.close();
}

一级缓存使用场景与限制

场景是否适用说明
同 Session 多次相同查询适用典型缓存命中场景
读写混合频繁不适用每次写操作清空缓存
多 Session 并发不适用一级缓存不跨 Session 共享
分布式环境不适用一级缓存是本地缓存,多实例不共享
需要实时数据不适用建议使用 STATEMENT 级别

要点总结

  • 一级缓存是 SqlSession 级别的本地缓存,底层基于 HashMap 实现
  • 缓存命中条件:同一 SqlSession、相同 SQL、相同参数、相同 RowBounds、无写操作
  • 写操作(INSERT/UPDATE/DELETE)和 commit/rollback/clearCache 都会清空一级缓存
  • localCacheScope 可配置为 SESSION(默认)或 STATEMENT 级别
  • SESSION 级别下缓存持续存在,STATEMENT 级别下每次查询后清空
  • 一级缓存不跨 Session 共享,不适合分布式环境

存放路径:D:\git2\jwdev\articles\MYBATIS\进阶\缓存机制\一级缓存机制.md

📝 发现内容有误?点击此处直接编辑

← 上一篇 延迟加载配置
下一篇 → 二级缓存配置
想查看更多题目和详细解析?
小程序提供完整的题库、模拟考试和详细解析
马上就来

长按或扫描二维码,立即体验

扫码体验小程序
马上就来
使用微信扫描二维码
立即体验完整题库