zoukankan      html  css  js  c++  java
  • mybatis——Executor

    映射器了解完了,sqlSession.select()实际上是Executor.query(),所以sql执行实际是executor执行的。

    一、BaseExecutor——实际的执行器(包含一级缓存实现)

    BaseExecutor是一个Executor的顶级抽象类,实现了Executor接口,定义sql执行的执行器。

    模板设计模式的运用,将一部分方法逻辑延迟给子类来实现。(专业术语:钩子)

    BaseExecutor有4个实现子类:

    下面介绍下BaseExecutor的实现

    1、变量与构造方法

    /* org.apache.ibatis.executor.BaseExecutor */
      protected Transaction transaction;//事务
      protected Executor wrapper;//一个指向自己(this)的指针
    
      protected ConcurrentLinkedQueue<DeferredLoad> deferredLoads;
      
      //mybatis的一级缓存
      protected PerpetualCache localCache;
      protected PerpetualCache localOutputParameterCache;
      //configuration对象
      protected Configuration configuration;
    
      protected int queryStack;
      //关闭状态哨兵
      private boolean closed;
    
      protected BaseExecutor(Configuration configuration, Transaction transaction) {
        this.transaction = transaction;
        this.deferredLoads = new ConcurrentLinkedQueue<DeferredLoad>();
        this.localCache = new PerpetualCache("LocalCache");
        this.localOutputParameterCache = new PerpetualCache("LocalOutputParameterCache");
        this.closed = false;
        this.configuration = configuration;
        this.wrapper = this;
      }

    2、主要方法

    由于没有注释,所以方法作用不是很清楚,简单分了下类

    /* org.apache.ibatis.executor.Executor */
      ResultHandler NO_RESULT_HANDLER = null;
    
      /* 
       * sql执行方法CRUD
       */
      int update(MappedStatement ms, Object parameter) throws SQLException;
    
      <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey cacheKey, BoundSql boundSql) throws SQLException;
    
      <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException;
    
      <E> Cursor<E> queryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds) throws SQLException;
    
      /*
       * BatchExecutor:执行所有的statement并关闭
    * ReuseExecutor:关闭所有复用的statemet
    */ List<BatchResult> flushStatements() throws SQLException; /* * 事务相关 */ void commit(boolean required) throws SQLException; void rollback(boolean required) throws SQLException; Transaction getTransaction(); /* * 缓存相关 */ CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql); boolean isCached(MappedStatement ms, CacheKey key); void clearLocalCache(); void deferLoad(MappedStatement ms, MetaObject resultObject, String property, CacheKey key, Class<?> targetType); /* * 自身状态相关 */ void close(boolean forceRollback); boolean isClosed(); void setExecutorWrapper(Executor executor);

    ① sql方法执行相关

    方法过多,选取一个doQuery执行ff

    /* 方法过多,选取一个方法研究 */
    /* org.apache.ibatis.executor.BaseExecutor#query(org.apache.ibatis.mapping.MappedStatement, java.lang.Object, org.apache.ibatis.session.RowBounds, org.apache.ibatis.session.ResultHandler) */
      public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
        //获取sql
        BoundSql boundSql = ms.getBoundSql(parameter);
        //创建缓存key
        CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
        return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
     }
    
      public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
        //sql校验是否存在
        ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
        //关闭哨兵,如果executor已关闭,抛出异常
        if (closed) {
          throw new ExecutorException("Executor was closed.");
        }
        //清除缓存(mybatis一级缓存)
        //queryStack == 0 :正在执行的select类型的sql的个数
        //并且 mappedStatement的flushCacheRequired属性为true,对应前面<sql>中的flushCache属性默认:<select> : false ; 非<select> true
        if (queryStack == 0 && ms.isFlushCacheRequired()) {
          clearLocalCache();
        }
        List<E> list;
        try {
          queryStack++;
          //根据cacheKey获取缓存
          list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
          if (list != null) {
            //缓存存在,由于默认preparedStatement,这里相当于空方法
            handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
          } else {
            //数据库查询
            list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
          }
        } finally {
          queryStack--;
        }
        if (queryStack == 0) {
          //延迟加载,什么时候需要延迟加载
          //关联映射时,select返回的resultMap中嵌套了子select查询,延迟加载将select与嵌套的子select查询的结果resultMap关联起来。
          //具体例子 订单查询的结果中嵌套订单详情查询。
          for (DeferredLoad deferredLoad : deferredLoads) {
            deferredLoad.load();
          }
          // issue #601
          deferredLoads.clear();
          if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
            // issue #482
            // 一级缓存的作用域是statement级别,清除缓存;默认的是session会话级别,不清楚缓存
            clearLocalCache();
          }
        }
        return list;
      }
    list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql):数据库查询
      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);
        }
        //放入缓存到baseExecutor.localCache中
        localCache.putObject(key, list);
        if (ms.getStatementType() == StatementType.CALLABLE) {
          localOutputParameterCache.putObject(key, parameter);
        }
        return list;
      }
    
      protected abstract <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql)
          throws SQLException;

    查看了下BaseExecutor类,钩子方法只有4个,都是与sql执行相关的方法。

      protected abstract int doUpdate(MappedStatement ms, Object parameter)
          throws SQLException;
      protected abstract List<BatchResult> doFlushStatements(boolean isRollback)
          throws SQLException;
      protected abstract <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql)
          throws SQLException;
      protected abstract <E> Cursor<E> doQueryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds, BoundSql boundSql)
          throws SQLException;

    这里看看各个子类是怎样执行的,还是以doQuery()方法为研究点

    SimpleExecutor(默认的BaseExecutor):每创建一个statement,执行完sql后,关闭statement

      public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
        Statement stmt = null;
        try {
          //获取configuration对象
          Configuration configuration = ms.getConfiguration();
          //应用configuration的设置--<settings>标签,驼峰命名等
          //另外还会执行拦截器
          StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
          stmt = prepareStatement(handler, ms.getStatementLog());
          //sql执行 statement.execute()
          //查询结果处理resultHandler.handleResultSet();result-->resultMap(处理嵌套sql)
          return handler.<E>query(stmt, resultHandler);
        } finally {
          closeStatement(stmt);
        }
      }
    
      private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
        Statement stmt;
        //获取连接
        Connection connection = getConnection(statementLog);
        //创建statement,并设置sql,默认是PreparedStatement
        stmt = handler.prepare(connection, transaction.getTimeout());
        //设置参数parameters
        handler.parameterize(stmt);
        return stmt;
      }
    
    /* org.apache.ibatis.session.Configuration#newStatementHandler */
    
      public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
        //应用configuration的一些设置
        StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
        //拦截器执行
        statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
        return statementHandler;
      }

    ReuseExecutor:创建一个statement,放入到一个statementMap<sql,statement>中实现statement复用,会设置一个事务超时时间,防止事务一直开启

    statement关闭时机:调用doFlushStatement()时,

    调用doFlushStatement()时机:① 显式调用:sqlSession.flushStatement(); ② 事务的提交、回退 sqlSession.commit();

    /* org.apache.ibatis.executor.ReuseExecutor#doQuery */
      public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
        Configuration configuration = ms.getConfiguration();
        StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
        Statement stmt = prepareStatement(handler, ms.getStatementLog());
        return handler.<E>query(stmt, resultHandler);
      }
    
      private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
        Statement stmt;
        BoundSql boundSql = handler.getBoundSql();
        String sql = boundSql.getSql();
        if (hasStatementFor(sql)) {
          //statement复用
          stmt = getStatement(sql);
          //事务有效时间
          applyTransactionTimeout(stmt);
        } else {
          Connection connection = getConnection(statementLog);
          stmt = handler.prepare(connection, transaction.getTimeout());
          putStatement(sql, stmt);
        }
        handler.parameterize(stmt);
        return stmt;
      }
    
      /* 
       * doFlushStatments关闭所有的statement
       * 执行时机:显式调用:sqlSession.flushStatements()
       */
      public List<BatchResult> doFlushStatements(boolean isRollback) throws SQLException {
        for (Statement stmt : statementMap.values()) {
          closeStatement(stmt);
        }
        statementMap.clear();
        return Collections.emptyList();
      }

    BatchExecutor:doQuery()创建一个statement,执行完sql,关闭statement,与SimpleExecutor不同主要在于doUpdate方法:

    BatchExecutor的doUpdate方法不是立即执行,而是将各种statement放入到一个list中,并且一个statement也可能存储多个sql(执行sql时必须连续,不连续,会生成两个statement)

    然后在调用doFlushStatement()时执行所有的statement并且关闭statement:

    调用doFlushStatement的时机:① 显式调用:sqlSession.flushStatement() ② 每次执行查询之前。③ 事务提交,sqlSession.commit()

    /* org.apache.ibatis.executor.SimpleExecutor#doUpdate 与doQuery没有区别 */
      public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
        Statement stmt = null;
        try {
          Configuration configuration = ms.getConfiguration();
          StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);
          stmt = prepareStatement(handler, ms.getStatementLog());
          return handler.update(stmt);
        } finally {
          closeStatement(stmt);
        }
      }
    
    /* org.apache.ibatis.executor.BatchExecutor#doUpdate */
      public int doUpdate(MappedStatement ms, Object parameterObject) throws SQLException {
        final Configuration configuration = ms.getConfiguration();
        final StatementHandler handler = configuration.newStatementHandler(this, ms, parameterObject, RowBounds.DEFAULT, null, null);
        final BoundSql boundSql = handler.getBoundSql();
        final String sql = boundSql.getSql();
        final Statement stmt;
        if (sql.equals(currentSql) && ms.equals(currentStatement)) {
          //当执行sql,sql与当前sql相同,并且mappedStatement也相同
          //刷新preparedStatement的事务时间
          //追加parameter到preparedStatement中
          //追加resultMap到batchResult中
          //即同一个prepareStatement中放了多个sql,这些sql仅参数不同
          int last = statementList.size() - 1;
          stmt = statementList.get(last);
          applyTransactionTimeout(stmt);
         handler.parameterize(stmt);//fix Issues 322
          BatchResult batchResult = batchResultList.get(last);
          batchResult.addParameterObject(parameterObject);
        } else {
          //初次执行sql时与当前sql或mappedStatement不同时
          //创建connect连接
          //创建statement并设置有效时间
          //记录当前sql、mappedStatement
          //记录添加statement到statementList中
          //记录添加resultMap到batchResultList中
          Connection connection = getConnection(ms.getStatementLog());
          stmt = handler.prepare(connection, transaction.getTimeout());
          handler.parameterize(stmt);    //fix Issues 322
          currentSql = sql;
          currentStatement = ms;
          statementList.add(stmt);
          batchResultList.add(new BatchResult(ms, sql, parameterObject));
        }
        //可以看出是将多个sql放入list实现的队列中(FIFO)
        //并没有执行,执行doFlushStatements()方法时才会执行
      // handler.parameterize(stmt);
        handler.batch(stmt);
        return BATCH_UPDATE_RETURN_VALUE;
      }
    
      /* 执行doFlushStatements的时机:比ReuseExecutor多一个。
       * 显式调用:sqlSession.flushStatements()
       * 执行doQuery时会先执行doFlushStatements()后查询
    * 事务提交:sqlSession.commit();
    */ public List<BatchResult> doFlushStatements(boolean isRollback) throws SQLException { try { List<BatchResult> results = new ArrayList<BatchResult>(); if (isRollback) { return Collections.emptyList(); } for (int i = 0, n = statementList.size(); i < n; i++) { Statement stmt = statementList.get(i); applyTransactionTimeout(stmt); BatchResult batchResult = batchResultList.get(i); try { batchResult.setUpdateCounts(stmt.executeBatch()); MappedStatement ms = batchResult.getMappedStatement(); List<Object> parameterObjects = batchResult.getParameterObjects(); KeyGenerator keyGenerator = ms.getKeyGenerator(); if (Jdbc3KeyGenerator.class.equals(keyGenerator.getClass())) { Jdbc3KeyGenerator jdbc3KeyGenerator = (Jdbc3KeyGenerator) keyGenerator; jdbc3KeyGenerator.processBatch(ms, stmt, parameterObjects); } else if (!NoKeyGenerator.class.equals(keyGenerator.getClass())) { //issue #141 for (Object parameter : parameterObjects) { keyGenerator.processAfter(this, ms, stmt, parameter); } } // Close statement to close cursor #1109 closeStatement(stmt); } catch (BatchUpdateException e) { StringBuilder message = new StringBuilder(); message.append(batchResult.getMappedStatement().getId()) .append(" (batch index #") .append(i + 1) .append(")") .append(" failed."); if (i > 0) { message.append(" ") .append(i) .append(" prior sub executor(s) completed successfully, but will be rolled back."); } throw new BatchExecutorException(message.toString(), e, results, batchResult); } results.add(batchResult); } return results; } finally { for (Statement stmt : statementList) { closeStatement(stmt); } currentSql = null; statementList.clear(); batchResultList.clear(); } }

    ResultLoaderMap.ClosedExecutor:ResultLoaderMap的一个私有内部类,仅供ResultLoaderMap对象使用,不支持sql操作,仅close状态控制。

        protected <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
          throw new UnsupportedOperationException("Not supported.");
        }

     ② 事务相关

      //开启事务
      public Transaction getTransaction() {
        if (closed) {
          throw new ExecutorException("Executor was closed.");
        }
        return transaction;
      }
       
      //提交事务
      public void commit(boolean required) throws SQLException {
        if (closed) {
          throw new ExecutorException("Cannot commit, transaction is already closed");
        }
        //清空缓存
        clearLocalCache();
        //清空statement,BatchExecutor会清空,清空前先执行
        flushStatements();
        if (required) {
          transaction.commit();
        }
      }
      
      //回退
      public void rollback(boolean required) throws SQLException {
        if (!closed) {
          try {
            //清空缓存
            clearLocalCache();
            //清空statement,BatchExecutor不会清空。
            flushStatements(true);
          } finally {
            if (required) {
              transaction.rollback();
            }
          }
        }
      }

    ③ 缓存相关

    /* org.apache.ibatis.executor.BaseExecutor */
      //生成缓存key,
    //用sql、parameters、environmentId、mappedStatementId、offset+limit生成一个hashCode
    public CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) { if (closed) { throw new ExecutorException("Executor was closed."); } CacheKey cacheKey = new CacheKey(); cacheKey.update(ms.getId()); cacheKey.update(rowBounds.getOffset()); cacheKey.update(rowBounds.getLimit()); cacheKey.update(boundSql.getSql()); List<ParameterMapping> parameterMappings = boundSql.getParameterMappings(); TypeHandlerRegistry typeHandlerRegistry = ms.getConfiguration().getTypeHandlerRegistry(); // mimic DefaultParameterHandler logic for (ParameterMapping parameterMapping : parameterMappings) { if (parameterMapping.getMode() != ParameterMode.OUT) { Object value; String propertyName = parameterMapping.getProperty(); if (boundSql.hasAdditionalParameter(propertyName)) { value = boundSql.getAdditionalParameter(propertyName); } else if (parameterObject == null) { value = null; } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) { value = parameterObject; } else { MetaObject metaObject = configuration.newMetaObject(parameterObject); value = metaObject.getValue(propertyName); } cacheKey.update(value); } } if (configuration.getEnvironment() != null) { // issue #176 cacheKey.update(configuration.getEnvironment().getId()); } return cacheKey; }  //判断sql是否有缓存 public boolean isCached(MappedStatement ms, CacheKey key) { return localCache.getObject(key) != null; } //清空缓存(mybatis一级缓存) public void clearLocalCache() { if (!closed) { localCache.clear(); localOutputParameterCache.clear(); } }

    ④ 关闭

      public void close(boolean forceRollback) {
        try {
          try {
            rollback(forceRollback);
          } finally {
            if (transaction != null) {
              transaction.close();
            }
          }
        } catch (SQLException e) {
          // Ignore.  There's nothing that can be done at this point.
          log.warn("Unexpected exception on closing transaction.  Cause: " + e);
        } finally {
          transaction = null;
          deferredLoads = null;
          localCache = null;
          localOutputParameterCache = null;
          closed = true;
        }
      }
    
      public boolean isClosed() {
        return closed;
      }
    
      public void setExecutorWrapper(Executor wrapper) {
        this.wrapper = wrapper;
      }

    3、总结

    ① Executor接口实现功能包括四个模块:sql执行模块、事务模块、缓存模块、状态模块(关闭状态)

    ② 4个功能的具体实现都在BaseExecutor抽象类中,注意以下说的缓存就是mybatis的一级缓存。

    • sql执行模块:先查询缓存,无缓存后执行sql,执行完成后,将结果放入到缓存中,具体执行sql的方法是钩子方法,延迟到子类中。
    • 事务模块:开启事务,事务提交,事务回退。需要注意的是事务提交和事务回退会清空一级缓存,还会清空statement
    • 缓存模块:生成cacheKey,判断是否存在缓存,清空缓存,chachKey的hashCode是多个属性联合生成的,包括id、sql、parameters、offset+limit、environmentId。
    • 状态模块:关闭Executor,清空所有资源;判断关闭状态。

    ③ 执行sql的具体实现延迟到了4个子类中,注意statement创建之前,会应用configuration对象的设置<setting>,及执行拦截器方法<plugin>中

    • SimpleExecutor:每次执行sql时,创建一个statement,执行sql,关闭statement
    • ReuseExecutor:初次执行sql时,创建一个statement,后续此sql的执行都是用此statement执行,调用doFlushStatement会清空所有statement。(statement复用)
    • BatchExecutor :执行update的sql时,创建一个statement放入到一个队列中,不立即执行,另外如果执行的sql与队尾的statement相同,将sql放入到statement中,注意是队尾statement。在调用doFlushStatement()时才会执行statement。执行query的sql时,先执行doFlushStatement(),然后与SimpleExecutor一致
    • closeExecutor:一个无用的Executor

    ④ doFlushStatement的时机:

    • SimpleExecutor:由于statement创建执行后直接关闭了,所以doFlushStatement方法无用。
    • ReuseExecutor:显式调用sqlSession.flushStatement;事务提交与回退;Executor关闭
    • BatchExecutor:显式调用sqlSession.flushStatement;事务提交;Executor关闭;每次查询sql执行之前

    关联映射时,延迟加载的使用。

    二、CachingExecutor——二级缓存执行器

    缓存执行器:mybatis二级缓存的实现,下面是CachingExecutor简单结构图

     CachingExecutor声明了一个Executor对象和一个缓存管理对象TransactionCacheManager。

    CachingExecutor执行sql时,会先使用先校验二级缓存,后使用BaseExecutor(内含一级缓存)执行sql。

    实际使用时:开启一次会话sqlSession时,创建的Executor是new CachingExecutor(new SimpleExecutor);

     1、变量与构造方法

      private final Executor delegate;
      private final TransactionalCacheManager tcm = new TransactionalCacheManager();
    
      public CachingExecutor(Executor delegate) {
        this.delegate = delegate;
        delegate.setExecutorWrapper(this);
      }

    2、方法实现

    CachingExecutor的大部分方法都使用策略模式调用内部变量executor的方法。选取几个重要的方法。

    ①缓存相关方法的实现:

    最终调用的都是BaseExecutor的方法

    意味着:一级缓存与二级缓存的cacheKey相同;CachingExecutor.clearLocalCache()清理的是一级缓存

    /* org.apache.ibatis.executor.CachingExecutor#createCacheKey */
      public CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) {
        //BaseExecutor.createCacheKey()
        return delegate.createCacheKey(ms, parameterObject, rowBounds, boundSql);
      }
      //调用baseExecutor.isCached
      public boolean isCached(MappedStatement ms, CacheKey key) {
        return delegate.isCached(ms, key);
      }
      //调用baseExecutor.clearLocalCache():注意这里是清空一级缓存
      public void clearLocalCache() {
        delegate.clearLocalCache();
      }

    ② 查询方法

      public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
        //跟一级缓存相比,生成的boundSql一致,生成的cacheKey一致
        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 {
        //获取二级缓存,二级缓存对应mappedStatement.cache
    //前面<mapper>标签解析时漏掉了,<mapper>里有<cache>标签
    //在解析<cache>标签时会创建一个cache对象放入到configuration.caches中同时会关联到mapperStatement.cache
    //二级缓存有多个cache实例,一个mapper.xml对应一个cache实例
    Cache cache = ms.getCache(); if (cache != null) { //mappedStatement.flushCacheRequired == true ,删除缓存mappedStatement.cache.clear() //复习下mappedStatement.flushCacheRequired,对应<sql>上的flushCache属性,默认<select>:false;非<select>:true flushCacheIfRequired(ms); //mappedStatement.useCache == true时才使用缓存 //复习下mappedStatement.userCache,对应<sql>上的useCache属性,默认<select>:true;非<select>:false if (ms.isUseCache() && resultHandler == null) { ensureNoOutParams(ms, boundSql); @SuppressWarnings("unchecked") //从缓存中获取cacheKey对应的value //mappedStatement.cache.get(cacheKey) List<E> list = (List<E>) tcm.getObject(cache, key); if (list == null) { //缓存中没有调用BaseExecutor执行sql,这里会查询一级缓存,若不存在,查询数据库,并更新一级缓存 list = delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql); //放入二级缓存中 tcm.putObject(cache, key, list); // issue #578 and #116 } return list; } } return delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql); }

    3、总结

    ① cacheExecutor采用策略模式,组合了一个BaseExecutor,执行的方法也是BaseExecutor的方法

    ② 由于CachingExecutor.createCacheKey()最终调用的是BaseExecutor.createCacheKey,所以一级缓存与二级缓存的cacheKey相同

    ③ 开启二级缓存后,sql执行先从查询二级缓存,后查询一级缓存,最后查询数据库。

    ④ 一级缓存 : BaseExecutor.localCache;二级缓存 : configuration.caches<namespace,cache>以namespace;两种cache最终数据存储在PerpetualCache.cache(Map类型)中

    ⑤ 虽然configuration默认的Executor是new CachingExecutor(new SimpleExecutor),但是由于在解析mapper.xml时,有<cache>或<cache-ref>标签才会创建一个cache到configuration.caches中,所以二级缓存默认是关闭的,需要到mapper.xml里面加入<cache><cache-ref>开启二级缓存

    ⑥ <cache>与<cache-ref>:<cache>创建一个LruCache实例,管理二级缓存的开启关闭,里面有一个PerpetualCache类型变量(装饰者模式)<cache-ref>作用主要是:一个mapper.xml可以引用另一mapper.xml的缓存达到缓存共享。

    补充:LruCache与CachingExecutor的UML图类似,为什么判断一个是装饰者模式,一个是策略模式。

    装饰者模式设计目的是原对象(PerpetualCache)的功能增强;策略默认设计目的原对象(Executor)抽象,可使用多个子对象(SimpleExecutor、ReuseExecutor、BatchExecutor)替换

  • 相关阅读:
    第一阶段SCRUM冲刺day07
    第一阶段SCRUM冲刺day06
    第一阶段SCRUM冲刺day05
    第一阶段SCRUM冲刺day04
    2020年第七周学习进度
    第一阶段SCRUM冲刺day03
    第一阶段SCRUM冲刺day02
    团队会议01
    2020年第六周学习进度
    TCP/IP Socket 的理解
  • 原文地址:https://www.cnblogs.com/wqff-biubiu/p/12449786.html
Copyright © 2011-2022 走看看