一级缓存
缓存体系

一级缓存命中场景
缓存一般是key-value的。所以一级缓存key的设计,就会影响到缓存的命中。

一级缓存源码分析
一级缓存命中
@Test
public void testFirstCache() throws IOException {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
User user = userMapper.selectById(2);
User user2 = userMapper.selectById(2);
System.out.println(user == user2);
}
同一个会话(SqlSession没有关闭),相同的查询sql,相同的查询参数,相同的statement(getMapper底层还是调用的SqlSession的方法),返回行是默认的也相同。

1.进入
MapperProxy
动态代理逻辑。

2.进入
MapperMethod
,根据不同的执行类型调用对应的SqlSession
方法执行逻辑。

3.进入
DefaultSqlSession
,这是SqlSession
的实现类。最终会进入该类的selectList()
方法。

4.进入
CachingExecutor
。这个执行器是处理二级缓存相关的逻辑,因为我们第一次查询没有提交,所以二级缓存不会生效。如果你没有开启二级缓存,也不会生效。我们现在主要研究一级缓存,先不关注二级缓存。
首先会进行二级缓存的判断查询,这里我开启了mybatis的二级缓存,所以进入了二级缓存逻辑里面。如果没有开启二级缓存,会直接交给下一个执行器执行。

5.进入
BaseExecutor
。这个是执行器的公共抽象类,定义了不同执行器之间的共性
。比如一级缓存就是在这里定义的。因为mybatis的一级缓存是默认打开的。也无法关闭,只能配置每次查询刷新缓存达到相同的目的。所以,每次在查数据库之前,都会先查询本地缓存(一级缓存)。因此,把这个逻辑抽离了出来。

6.进入
PerpetualCache
。这个是处理一级缓存的类。类名翻译过来是永久缓存
。也就是说,一级缓存是一直存在的。除非进行修改操作或者关闭程序。
查看
BaseExecutor
中的update()
方法,发现的确有清空本地缓存的操作:

也就是说,只要在同一个会话中,会话没有关闭之前。只要有修改操作,这个会话中的一级缓存就会被清空。这和我们之前了解到的一级缓存命中情况是相符的。
7.
BaseExecutor
中,因为现在我们的查询是符合一级缓存的条件的,所以会查询到数据:

对于如何从数据库查询,可以查看Mybatis执行器中的SqlSession查询流程。
从样就查询到了数据,而且是命中一级缓存的情况。接下来就会把数据按调用链路进行返回。最后我们就从一级缓存中查询到了数据。

不走一级缓存
如果不满足一级缓存的获取条件,或者是第一次查询,都是会直接从数据库中查询的,查询到数据之后会填充到一级缓存。代码在上面的第7步,BaseExecutor
中的queryFromDatabase()
方法:

BaseExecutor
中
// doQuery()方法是BaseExecutor定义的抽象方法,负责查询数据库,BaseExecutor只是定义,实现交给具体的子类
// 因此查询数据库是由BaseExecutor的子类实现的,常用的有SimpleExecutor(默认)、ReuseExecutor(可重用执行器:预编译)、BatchExecutor(批量处理执行器)
protected abstract <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql)
throws SQLException;
private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
List<E> list;
localCache.putObject(key, EXECUTION_PLACEHOLDER);
try {
list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
} finally {
localCache.removeObject(key);
}
// 数据库中查询的数据list,会放入一级缓存(HashMap)
localCache.putObject(key, list);
if (ms.getStatementType() == StatementType.CALLABLE) {
localOutputParameterCache.putObject(key, parameter);
}
return list;
}
具体执行器中的doQuery()方法实现,可以查看Mybatis执行器中的SqlSession查询流程分析。
一级缓存执行流程
