在应用运行过程中,我们有可能在一次数据库会话中,执行多次查询条件完全相同的 SQL,MyBatis 提供了一级缓存的方案优化这部分场景,如果是相同的 SQL 语句,会优先命中一级缓存,避免直接对数据库进行查询,提高性能。具体执行过程如下图所示。
每个 SqlSession 中持有了 Executor,每个 Executor 中有一个 LocalCache。当用户发起查询时,MyBatis 根据当前执行的语句生成 MappedStatement,在 Local Cache 进行查询,如果缓存命中的话,直接返回结果给用户,如果缓存没有命中的话,查询数据库,结果写入 Local Cache,最后返回结果给用户。具体实现类的类关系图如下图所示。
一级缓存配置我们来看看如何使用 MyBatis 一级缓存。开发者只需在MyBatis的配置文件中,添加如下语句,就可以使用一级缓存。共有两个选项,SESSION或者 STATEMENT,默认是 SESSION 级别,即在一个 MyBatis 会话中执行的所有语句,都会共享这一个缓存。一种是 STATEMENT 级别,可以理解为缓存只对当前执行的这一个 Statement 有效。
<setting value="SESSION"/> 一级缓存实验接下来通过实验,了解 MyBatis 一级缓存的效果,每个单元测试后都请恢复被修改的数据。
首先是创建示例表 student,创建对应的 POJO 类和增改的方法,具体可以在 entity 包和 mapper 包中查看。
CREATE TABLE `student` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, `name` varchar(200) COLLATE utf8_bin DEFAULT NULL, `age` tinyint(3) unsigned DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 COLLATE=utf8_bin;在以下实验中,id为1的学生名称是凯伦。
实验1开启一级缓存,范围为会话级别,调用三次 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)); }执行结果:
我们可以看到,只有第一次真正查询了数据库,后续的查询使用了一级缓存。
实验2增加了对数据库的修改操作,验证在一次数据库会话中,如果对数据库发生了修改操作,一级缓存是否会失效。
@Test 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(); }执行结果: