1. 首先来执行一下,看看什么时候会触发一级缓存
2.1 相同的sql与参数
SqlSession sqlSession = factory.openSession(true);
NameMapper mapper = sqlSession.getMapper(NameMapper.class);
T00NameDO name1 = mapper.selectById(1L);
T00NameDO name2 = mapper.selectById(1L);
System.out.println(name1 == name2);
==> Preparing: select * from t00_name where id=?
==> Parameters: 1(Long)
<== Columns: id, name
<== Row: 1, test
<== Total: 1
true
2.2 相同的sql与不同的参数
SqlSession sqlSession = factory.openSession(true);
NameMapper mapper = sqlSession.getMapper(NameMapper.class);
T00NameDO name1 = mapper.selectById(1L);
T00NameDO name2 = mapper.selectById(2L);
System.out.println(name1 == name2);
==> Preparing: select * from t00_name where id=?
==> Parameters: 1(Long)
<== Columns: id, name
<== Row: 1, test
<== Total: 1
==> Preparing: select * from t00_name where id=?
==> Parameters: 2(Long)
<== Total: 0
false
2.3 相同的sql与参数,不同的statementId
SqlSession sqlSession = factory.openSession(true);
NameMapper mapper = sqlSession.getMapper(NameMapper.class);
T00NameDO name1 = mapper.selectById(1L);
T00NameDO name2 = mapper.selectById1(1L);
System.out.println(name1 == name2);
==> Preparing: select * from t00_name where id=?
==> Parameters: 1(Long)
<== Columns: id, name
<== Row: 1, test
<== Total: 1
==> Preparing: select * from t00_name where id=?
==> Parameters: 1(Long)
<== Columns: id, name
<== Row: 1, test
<== Total: 1
false
2.4 相同的sql与参数,statementId,不同的会话
SqlSession sqlSession1 = factory.openSession(true);
T00NameDO name1 = sqlSession1.getMapper(NameMapper.class).selectById(1L);
SqlSession sqlSession2 = factory.openSession(true);
T00NameDO name2 = sqlSession2.getMapper(NameMapper.class).selectById(1L);
System.out.println(name1 == name2);
==> Preparing: select * from t00_name where id=?
==> Parameters: 1(Long)
<== Columns: id, name
<== Row: 1, test
<== Total: 1
==> Preparing: select * from t00_name where id=?
==> Parameters: 1(Long)
<== Columns: id, name
<== Row: 1, test
<== Total: 1
false
2.5 相同的sql与参数,statementId,相同会话,不同的分页参数
SqlSession sqlSession = factory.openSession(true);
List<T00NameDO> nameList1 = sqlSession.selectList("com.lr.mybatis.NameMapper.selectById", 1L, new RowBounds(0, 1));
List<T00NameDO> nameList2 = sqlSession.selectList("com.lr.mybatis.NameMapper.selectById", 1L, new RowBounds(0, 2));
System.out.println(nameList1.get(0) == nameList2.get(0));
==> Preparing: select * from t00_name where id=?
==> Parameters: 1(Long)
<== Columns: id, name
<== Row: 1, test
==> Preparing: select * from t00_name where id=?
==> Parameters: 1(Long)
<== Columns: id, name
<== Row: 1, test
<== Total: 1
false
2.6 相同的sql与参数,statementId,相同会话,分页参数,sqlsession清空缓存
SqlSession sqlSession = factory.openSession(true);
NameMapper mapper = sqlSession.getMapper(NameMapper.class);
T00NameDO name1 = mapper.selectById(1L);
sqlSession.clearCache();
T00NameDO name2 = mapper.selectById(1L);
System.out.println(name1 == name2);
==> Preparing: select * from t00_name where id=?
==> Parameters: 1(Long)
<== Columns: id, name
<== Row: 1, test
<== Total: 1
==> Preparing: select * from t00_name where id=?
==> Parameters: 1(Long)
<== Columns: id, name
<== Row: 1, test
<== Total: 1
false
2.7 相同的sql与参数,statementId,相同会话,分页参数,sqlsession不清空缓存,添加注解@Options(flushCache = Options.FlushCachePolicy.TRUE)
@Select({" select * from t00_name where id=#{1}"})
@Options(flushCache = Options.FlushCachePolicy.TRUE)
T00NameDO selectById(Long id);
SqlSession sqlSession = factory.openSession(true);
NameMapper mapper = sqlSession.getMapper(NameMapper.class);
T00NameDO name1 = mapper.selectById(1L);
T00NameDO name2 = mapper.selectById(1L);
System.out.println(name1 == name2);
==> Preparing: select * from t00_name where id=?
==> Parameters: 1(Long)
<== Columns: id, name
<== Row: 1, test
<== Total: 1
==> Preparing: select * from t00_name where id=?
==> Parameters: 1(Long)
<== Columns: id, name
<== Row: 1, test
<== Total: 1
false
2.8 相同的sql与参数,statementId,相同会话,分页参数,sqlsession不清空缓存,不添加注解@Options(flushCache = Options.FlushCachePolicy.TRUE),查询中间插入修改
@Select({" select * from t00_name where id=#{1}"})
T00NameDO selectById(Long id);
@Update("update t00_name set name = #{name} where id = #{id}")
int setName(@Param("id") Long id, @Param("name") String name);
SqlSession sqlSession = factory.openSession(true);
NameMapper mapper = sqlSession.getMapper(NameMapper.class);
T00NameDO name1 = mapper.selectById(1L);
mapper.setName(1L, "lr");//哪怕修改的不是改行的数据仍旧不会命中缓存
T00NameDO name2 = mapper.selectById(1L);
System.out.println(name1 == name2);
==> Preparing: select * from t00_name where id=?
==> Parameters: 1(Long)
<== Columns: id, name
<== Row: 1, test
<== Total: 1
==> Preparing: update t00_name set name = ? where id = ?
==> Parameters: lr(String), 1(Long)
<== Updates: 1
==> Preparing: select * from t00_name where id=?
==> Parameters: 1(Long)
<== Columns: id, name
<== Row: 1, lr
<== Total: 1
false
2.9 相同的sql与参数,statementId,相同会话,分页参数,sqlsession不清空缓存,不添加注解@Options(flushCache = Options.FlushCachePolicy.TRUE),中间不插入修改,配置缓存作用域LocalCacheScope.STATEMENT
configuration.setLocalCacheScope(LocalCacheScope.STATEMENT);
SqlSession sqlSession = factory.openSession(true);
NameMapper mapper = sqlSession.getMapper(NameMapper.class);
T00NameDO name1 = mapper.selectById(1L);
T00NameDO name2 = mapper.selectById(1L);
System.out.println(name1 == name2);
==> Preparing: select * from t00_name where id=?
==> Parameters: 1(Long)
<== Columns: id, name
<== Row: 1, lr
<== Total: 1
==> Preparing: select * from t00_name where id=?
==> Parameters: 1(Long)
<== Columns: id, name
<== Row: 1, lr
<== Total: 1
false
2.10 相同的sql与参数,statementId,相同会话,分页参数,sqlsession不清空缓存,不添加注解@Options(flushCache = Options.FlushCachePolicy.TRUE),中间不插入修改,不配置缓存作用域LocalCacheScope.STATEMENT,sqlSession进行commit/rollback操作
SqlSession sqlSession = factory.openSession(true);
NameMapper mapper = sqlSession.getMapper(NameMapper.class);
T00NameDO name1 = mapper.selectById(1L);
sqlSession.commit();
sqlSession.rollback();
T00NameDO name2 = mapper.selectById(1L);
System.out.println(name1 == name2);
==> Preparing: select * from t00_name where id=?
==> Parameters: 1(Long)
<== Columns: id, name
<== Row: 1, lr
<== Total: 1
==> Preparing: select * from t00_name where id=?
==> Parameters: 1(Long)
<== Columns: id, name
<== Row: 1, lr
<== Total: 1
false
2. 基本结构
从上面的测试我们可以看出,一级缓存命中的条件还是比较多的,可以做一下归类
- sqlsession
- statementId
- sql
- sql的参数
- 分页参数
- flushCache
- LocalCacheScope
- commit/rollback
- 中间有update操作
- resultHandle
那么显而易见的mybatis的一级缓存是针对于Sesision级别的,每次于数据库的会话都会创建出一个sqlsession,执行器executor会在每个sqlsession中被new出来,一级缓存我们都知道存在于基础执行器BaseExecutor中,
所以一级缓存与sqlsession的关系就是一对一
一级缓存在执行器中的结构为PerpetualCache,这个类对象中维护了一个HashMap
接下来我们知道缓存实际上是个HashMap,那么它的Key是什么呢?
我们从基础执行器BaseExecuse的query方法入手
一共有两个query方法,可以从代码中看出,最终调用的是下面的query方法,而上面的方法获取了一级缓存的Key,我们先从CacheKey的结构入手
CacheKey的update方法
CacheKey的equals方法
那么接下来看看CacheKey是如何进行创建的
我们可以看到一共update了6次,msId+offset+limit+sql+parameterValue+environmentId,刚好符合我们上述2~5的情况,那么剩下的情况我们在分析一下,从实际执行的query方法可以看到这么一行,
当我们加上这个注解的时候,那么会清空一级缓存,所以无法命中6
if (queryStack == 0 && ms.isFlushCacheRequired()) {
clearLocalCache();
}
7的情况就根据下面的代码分析
if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
// issue #482
clearLocalCache();
}
8的情况为以下代码,执行的时候我们也可以看到清空本地缓存
9的情况需要去参考update方法
10的情况可以从主流程中分析,如果ResultHandler为空,才会命中缓存。这个留着后面再做学习