美文网首页mybatis
mybatis源码分析-selectOne-03

mybatis源码分析-selectOne-03

作者: 愤怒的奶牛 | 来源:发表于2019-10-08 20:28 被阅读0次

    上篇文章我们分析到了 CachingExecutor ,本文我们就来详细的分析一下 CachingExecutor 。为了方便阅读我们再来看一下 Executor 的类图

    Executor.png
    1.1 CachingExecutor
    public class CachingExecutor implements Executor {
    
      private final Executor delegate;
      private final TransactionalCacheManager tcm = new TransactionalCacheManager();
    // 委派模式 ,持有 具体干活的实现类,自己一定不会干活,
    //其实也可以看成是 静态代理的特殊情况,就是代理对象什么也不干,活都让 被代理的对象干了
      public CachingExecutor(Executor delegate) {
        this.delegate = delegate;// delegate -> SimpleExecutor 
        delegate.setExecutorWrapper(this);
      }
    ....
      @Override
      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);
      }
    
    .....
      @Override
      public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
          throws SQLException {
        Cache cache = ms.getCache();
        if (cache != null) {
          flushCacheIfRequired(ms);
          if (ms.isUseCache() && resultHandler == null) {
            ensureNoOutParams(ms, boundSql);
            @SuppressWarnings("unchecked")
            List<E> list = (List<E>) tcm.getObject(cache, key);
            if (list == null) {
    // 执行 查询的 是 具体的实现了,CachingExecutor 自己不会有操作数据库的逻辑
              list = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
              tcm.putObject(cache, key, list); // issue #578 and #116
            }
            return list;
          }
        }
    // 执行 查询的 是 具体的实现了,CachingExecutor 自己不会有操作数据库的逻辑
        return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
      }
    

    上面这段逻辑就是在调用 delegate 对象的 query() 方法,我们先不管 if 条件这些判断,避开对我们看主线代码的影响,都是 走的 下面这句话:

    delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
    

    那么现在所有的关键都在这句话了,通过前面两篇文章的分析,我们知道 delegate -> SimpleExecutor 。接下来我们要看具体的 数据库操作 就要去 SimpleExecutor 了。

    1.2 SimpleExecutor

    从上面的类图我们知道 SimpleExecutor -> BaseExecutor-> Executor 。
    SimpleExecutor 继承了 BaseExecutor ,BaseExecutor 是对接口的抽象实现,这里又有模板设计模式。我们先来看下代码:

    /**
     * @author Clinton Begin
     */
    public class SimpleExecutor extends BaseExecutor {
    
      public SimpleExecutor(Configuration configuration, Transaction transaction) {
        super(configuration, transaction);
      }
    
      @Override
      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);
        }
      }
    
      @Override
      public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
        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);
        }
      }
    
      @Override
      protected <E> Cursor<E> doQueryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds, BoundSql boundSql) throws SQLException {
        Configuration configuration = ms.getConfiguration();
        StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, null, boundSql);
        Statement stmt = prepareStatement(handler, ms.getStatementLog());
        stmt.closeOnCompletion();
        return handler.queryCursor(stmt);
      }
    
      @Override
      public List<BatchResult> doFlushStatements(boolean isRollback) {
        return Collections.emptyList();
      }
    
      private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
        Statement stmt;
        Connection connection = getConnection(statementLog);
        stmt = handler.prepare(connection, transaction.getTimeout());
        handler.parameterize(stmt);
        return stmt;
      }
    
    }
    
    

    上面是 SimpleExecutor 对所有方法,我们发现并没有我们 关心的 query() 方法,从类的关系我们也能看出,query 方法应该是在 父类BaseExecutor 中,前面我们说到这里有 模式设计模式,我们就来分析下是怎么回事:先看 BaseExecutor

    /**
     * @author Clinton Begin
     */
    public abstract class BaseExecutor implements Executor {
    
    .....
      @Override
      public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
        BoundSql boundSql = ms.getBoundSql(parameter);
        CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
        return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
      }
    
      @SuppressWarnings("unchecked")
      @Override
      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++;
          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();
          }
          // issue #601
          deferredLoads.clear();
          if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
            // issue #482
            clearLocalCache();
          }
        }
        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;
      }
    
    ......
    //抽象方法,子类实现
      protected abstract <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql)
          throws SQLException;
    
    }
    .....
    

    上面省略了部分代码,只保留了关键 部分。doQuery() 是抽象 方法,我们在 SimpleExecutor 中看到了 doQuery() 方法的实现,

    @Override
      public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
        Statement stmt = null;
        try {
          Configuration configuration = ms.getConfiguration();
    //     StatementHandler 负责查询,结果解析封装
     StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
          stmt = prepareStatement(handler, ms.getStatementLog());
          return handler.query(stmt, resultHandler);
        } finally {
          closeStatement(stmt);
        }
      }
    

    上面就 是具体的 doQuery() 方法的实现,从这里我么可以看出 doQuery() 中的 StatementHandler 是完成最后查询和结果封装的接口。到这里我们 分析了 CachingExecutor 和 SimpleExecutor ,源码里面使用了 委派模式和 模板方法,如果你对模板方法有点晕,我们来解释一下,当你在调用 SimpleExecutor 的 query() 方法时,由于子类没有定义该方法,该方法是从父类继承过来的,所以 调用的是父类的query() 方法,而在 父类的query() 方法中 调用的是 抽象的doQuery() 方法,子类实现了 doQuery() 方法,所以doQuery() 方法在 SimpleExecutor.query() 时被回调了,文字解释有点苍白,可以自己写一个demo 就明白了,这也是抽象的很好体现。但是委派模式在这里到底有啥好处,目前我也还不能体会其奥义。后面会接着分析 StatementHandler 接口,看看它又干了些啥事。

    相关文章

      网友评论

        本文标题:mybatis源码分析-selectOne-03

        本文链接:https://www.haomeiwen.com/subject/giripctx.html