MyBatis 一级缓存实现详解及使用注意事项

一级缓存介绍

在应用运行过程中,我们有可能在一次数据库会话中,执行多次查询条件完全相同的SQL,MyBatis提供了一级缓存的方案优化这部分场景,如果是相同的SQL语句,会优先命中一级缓存,避免直接对数据库进行查询,提高性能。具体执行过程如下图所示。

《MyBatis 一级缓存实现详解及使用注意事项》

每个SqlSession回话中会创建Executor执行器,每个Executor执行器中有一个Local Cache。当用户发起查询时,MyBatis根据当前执行的语句生成MappedStatement,在Local Cache进行查询,如果缓存命中的话,直接返回结果给用户,如果缓存没有命中的话,查询数据库,结果写入Local Cache,最后返回结果给用户。

一级缓存配置

我们来看看如何使用MyBatis一级缓存。开发者只需在MyBatis的配置文件中,添加如下语句,就可以使用一级缓存。共有两个选项,SESSION或者STATEMENT,默认是SESSION级别,即在一个MyBatis会话中执行的所有语句,都会共享这一个缓存。一种是STATEMENT级别,可以理解为缓存只对当前执行的这一个Statement有效。

<setting name="localCacheScope" value="SESSION"/>

一级缓存实验

开启一级缓存,范围为会话级别,调用三次getStudentById,代码如下所示:

public void getStudentById() throws Exception {
    SqlSession sqlSession = factory.openSession(true); // 自动提交事务
    StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);
    System.out.println(studentMapper.getStudentById(1)); // 查询数据库
    System.out.println(studentMapper.getStudentById(1)); // 查询缓存
    System.out.println(studentMapper.getStudentById(1)); // 查询缓存
}
public void addStudent() throws Exception {
    SqlSession sqlSession = factory.openSession(true); // 自动提交事务
    StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);
    System.out.println(studentMapper.getStudentById(1)); // 查询数据库
    System.out.println(studentMapper.addStudent(buildStudent())); // 更新数据
    System.out.println(studentMapper.getStudentById(1)); // 查询数据库
    sqlSession.close();
}
public void testLocalCacheScope() throws Exception {
    SqlSession sqlSession1 = factory.openSession(true); 
    SqlSession sqlSession2 = factory.openSession(true); 

    StudentMapper studentMapper = sqlSession1.getMapper(StudentMapper.class);
    StudentMapper studentMapper2 = sqlSession2.getMapper(StudentMapper.class);

    System.out.println(studentMapper.getStudentById(1)); // 查询数据库
    System.out.println(studentMapper.getStudentById(1)); // 查询缓存
    System.out.println(studentMapper2.updateStudentName("小岑",1)); // 更新数据
    System.out.println(studentMapper.getStudentById(1)); // 查询缓存,脏数据
    System.out.println(studentMapper2.getStudentById(1)); // 查询数据库
}

一级缓存工作流程

一级缓存执行的时序图,如下图所示。

《MyBatis 一级缓存实现详解及使用注意事项》

总结

  1. MyBatis一级缓存的生命周期和SqlSession一致。
  2. MyBatis一级缓存内部设计简单,只是一个没有容量限定的HashMap,在缓存的功能性上有所欠缺。
  3. MyBatis的一级缓存最大范围是SqlSession内部,有多个SqlSession或者分布式的环境下,数据库写操作会引起脏数据,所以建议设定缓存级别为Statement
点赞