mybatis源码阅读 (五)

mybatis中的缓存,有一个疑问为什么一级缓存需要先放一个占位值,查询到结果后再移除,放入真正的值???代码标红处

1、二级缓存

public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
    BoundSql boundSql = ms.getBoundSql(parameterObject);
    CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
    return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
  }

  public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
      throws SQLException {
    // 获取当前mapper的缓存
    Cache cache = ms.getCache();
    if (cache != null) {
      flushCacheIfRequired(ms);
      if (ms.isUseCache() && resultHandler == null) {
        ensureNoOutParams(ms, parameterObject, boundSql);
        @SuppressWarnings("unchecked")
        // 查询的key是由 查询的sql 、参数、 分页的 start,limit组合成的唯一的key
        //在当前的mapper 缓存中的 查询的 key对应的结果
        List<E> list = (List<E>) tcm.getObject(cache, key);
        if (list == null) {
          list = delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
          //放到缓存中
          tcm.putObject(cache, key, list); // issue #578. Query must be not synchronized to prevent deadlocks
        }
        return list;
      }
    }
    return delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
  }

这也是为什么说二级缓存是mapper级别的

2、一级缓存

public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
    ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
    if (closed) throw new ExecutorException("Executor was closed.");
    if (queryStack == 0 && ms.isFlushCacheRequired()) {
      clearLocalCache();
    }
    List<E> list;
    try {
      queryStack++;
      // 从缓存中取值 这里的localCache 其实是PerpetualCache,getObject其实是从cache属性(是一个HashMap)中取值
      list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
      if (list != null) {
        handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
      } else {
        list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
      }
    } finally {
      queryStack--;
    }
    if (queryStack == 0) {
      for (DeferredLoad deferredLoad : deferredLoads) {
        deferredLoad.load();
      }
      deferredLoads.clear(); // issue #601
      if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
        clearLocalCache(); // issue #482
      }
    }
    return list;
  }

  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;
  }

原文地址:https://www.cnblogs.com/jas0/p/9551757.html

时间: 2024-08-02 20:52:37

mybatis源码阅读 (五)的相关文章

Mybatis源码阅读之--整体执行流程

Mybatis执行流程分析 Mybatis执行SQL语句可以使用两种方式: 使用SqlSession执行update/delete/insert/select操作 使用SqlSession获得对应的Mapper,然后调用mapper的相应方法执行语句 其中第二种方式获取Mapper的流程在前面已经解析过,请查看文章Mybatis源码阅读之--Mapper执行流程 其实这个方法最后的MapperMthod也是调用SqlSession的相应方法执行增删该的操作,这边文章主要介绍SqlSession执

MyBatis源码阅读

编程式开发使用MyBatis 在研究MyBatis源码之前,先来看下单独使用MyBatis来查询数据库时是怎么做的: 1 InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml"); 2 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); 3 SqlSession s

mybatis源码阅读-高级用法(二)

新建学生表和学生证表 --学生表 CREATE TABLE student( id INT NOT NULL PRIMARY KEY AUTO_INCREMENT COMMENT 'id', `name` VARCHAR(20) NOT NULL COMMENT '姓名', `age` INT NOT NULL COMMENT '年龄', sex INT NOT NULL COMMENT '性别 1 男 0 女', cid INT NOT NULL COMMENT '班级id', cardId

mybatis源码阅读-SqlSessionFactory(三)

我们的一个mybatis程序 SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder(); //返回的DefaultSqlSessionFactory的实例 SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder .build(ClassLoader.getSystemResourceAsStream("mybatis.x

Mybatis源码阅读之--本地(一级)缓存实现原理分析

前言: Mybatis为了提升性能,内置了本地缓存(也可以称之为一级缓存),在mybatis-config.xml中可以设置localCacheScope中可以配置本地缓存的作用域,包含两个值session和statement,其中session选项表示本地缓存在整个session都有效,而statement只能在一条语句中有效(这条语句有嵌套查询--nested query/select). 下面分析一下mybatis本地缓存的实现原理. 本地缓存是在Executor内部构建,Executor

SDWebImage源码阅读(五)SDImageCacheConfig/SDImageCache(下)

在上篇中已经了解分析了 SDImageCache.h 文件中所有的方法和属性.大概对 SDImageCache 能实现的功能已经有了全面的认识.在这篇则着重学习研究这些功能的实现过程和实现原理. SDImageCache  是 SDWebImage 里面用来做缓存的类,虽然只是针对的图片的缓存,但是其实在 iOS 开发甚至在程序开发中,缓存类对缓存文件的类型区别而要单独针对文件类型做处理的需要并不多,不管是图片的缓存还是别的类型文件的缓存,它们在缓存原理上是基本一致的.通过对 SDImageCa

JDK源码阅读(五)java.io.Serializable接口

package java.io; public interface Serializable { } (1)实现Serializable接口的类,将会被提示提供一个 serialVersionUID 注意点一.序列化时为了保持版本的兼容性,即在版本升级时反序列化仍保持对象的唯一性. 有两种生成方式:              ①一个是默认的1L,比如:private static final long serialVersionUID = 1L;              ②一个是根据类名.接口

mybatis源码阅读(三)

SqlSesion怎么获取一个Mapper? 一个Mapper接口没有一个实现类怎么能够实例化? public <T> T getMapper(Class<T> type) { // 通过 configuration 的getMapper方法获取Mapper对象 return configuration.<T>getMapper(type, this); } public <T> T getMapper(Class<T> type, SqlSes

mybatis源码阅读(二)

通过SqlSessionFactory 创建 SqlSession // 通过SqlSessionFactory 获取创建一个SqlsessionSqlSession SqlSession sqlSession = sqlSessionFactory.openSession(); public SqlSession openSession() { return openSessionFromDataSource(configuration.getDefaultExecutorType(), nu