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 二级缓存

 

待补充

 

posted @ 2022-01-09 23:27  zhenjingcool  阅读(74)  评论(0编辑  收藏  举报