Mybatis源码学习(四)一级缓存和二级缓存
为了查看效果,当执行数据库时,我们打印一句话,我们修改SimpleExecutor.java的doQuery方法,打印一句话,如下所示。
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException { System.out.println("查询数据库"); Statement stmt = null; try { Configuration configuration = ms.getConfiguration(); StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql); stmt = prepareStatement(handler, ms.getStatementLog()); return handler.query(stmt, resultHandler); } finally { closeStatement(stmt); } }
1 一级缓存
1.1 演示
修改Mybatis源码学习(一)中的代码
public class MybatisMain { public static void main(String[] args) throws IOException { String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); SqlSession sqlSession = sqlSessionFactory.openSession(); Blog blog1 = sqlSession.selectOne("org.mybatis.example.BlogMapper.selectBlog", 101); System.out.println(blog1.getContext()); Blog blog2 = sqlSession.selectOne("org.mybatis.example.BlogMapper.selectBlog", 101); System.out.println(blog2.getContext()); } }
sqlSession.selectOne执行了两次,我们查看执行结果
1.2 一级缓存失效的场景
我们创建两个SqlSession,分别进行查询
public class MybatisMain { public static void main(String[] args) throws IOException { String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); SqlSession sqlSession = sqlSessionFactory.openSession(); SqlSession sqlSession_ = sqlSessionFactory.openSession(); Blog blog1 = sqlSession.selectOne("org.mybatis.example.BlogMapper.selectBlog", 101); System.out.println(blog1.getContext()); Blog blog2 = sqlSession.selectOne("org.mybatis.example.BlogMapper.selectBlog", 101); System.out.println(blog2.getContext()); Blog blog3 = sqlSession_.selectOne("org.mybatis.example.BlogMapper.selectBlog", 101); System.out.println(blog3.getContext()); } }
我们查看执行结果,两个SqlSession都进行了数据库的查询。
一级缓存是Mybatis默认开启的,二级缓存是默认关闭的,上述例子中,一级缓存作用范围是在一个SqlSession中,超过这个SqlSession一级缓存将会失效。
1.3 源码解析
当我们执行 blogMapper.query(101) 时,通过代理类 MapperProxy 的invoke方法调用 MapperMethod.execute() 方法,再调用了 DefaultSqlSession.selectOne() 方法,然后先尝试走二级缓存 CachingExecutor.query() 在这里,首先看一下是否开启了二级缓存,如果开启了,则尝试从二级缓存拿数据;如果没有开启,则执行 BaseExecutor.query() ,在这个方法里尝试走一级缓存;如果一级缓存中没有,则查询数据库获取数据,并且把查询结果存入一级缓存。
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); } localCache.putObject(key, list); if (ms.getStatementType() == StatementType.CALLABLE) { localOutputParameterCache.putObject(key, parameter); } return list; }
当执行更新操作时,会清空一级缓存
public int update(MappedStatement ms, Object parameter) throws SQLException { ErrorContext.instance().resource(ms.getResource()).activity("executing an update").object(ms.getId()); if (closed) { throw new ExecutorException("Executor was closed."); } clearLocalCache(); return doUpdate(ms, parameter); }
当sqlSession断开或者flush时,也会清空一级缓存
2 二级缓存
待补充