Mybatis源码分析(六):执行sql

获取SqlSession后,下一步就是执行sql.

User user=sqlSession.selectOne("last.soul.mapper.UserMapper.selectById",map);

DefaultSqlSession的主要功能就是实现增删改查功能,以及它们的重载方法。就查询来说,最后都会调用select方法,然后改装成selectOne,selectMap等,代码如下:

/**
   * 
   * @param statement sql语句ID=xxxMapper.xml文件中的namespace+sql标签的id.
   *                  如:last.soul.mapper.UserMapper.selectById
   * @param parameter sql的参数
   * @param rowBounds 分页信息
   * @param <E>
   * @return
   */
  @Override
  public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
    try {
      //sql相关消息的包装对象,比如:sql语句,返回类型,是否使用缓存等
      MappedStatement ms = configuration.getMappedStatement(statement);
      //执行sql
      return executor.query(ms, wrapCollection(parameter)/*包装集合类型参数*/, rowBounds, Executor.NO_RESULT_HANDLER);
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }

由上文得知executor是CachingExecutor,执行的是CachingExecutor的query方法。

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) {
          list = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
          tcm.putObject(cache, key, list); // issue #578 and #116
        }
        return list;
      }
    }
    //delegate是SimpleExecutor对象
    return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
  }

query方法中主要逻辑是判断是否有缓存,有缓存返回缓存值,否则执行delegate对象的query方法,即SimpleExecutor对象的query方法,然而你会发现该类中没有些方法,那它执行的就是他的父类BaseExecutor的query方法。query方法又会调用它的私有方法queryFromDatabase。

Mybatis源码分析(六):执行sql

queryFromDatabase源代码如下:

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

其中最关键的代码就是调用了子类(SimpleExecutor类,CachingExecutor类没有实现该方法)的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 handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
      stmt = prepareStatement(handler, ms.getStatementLog());
      return handler.query(stmt, resultHandler);
    } finally {
      closeStatement(stmt);
    }
  }

这个方法的主要逻辑是创建StatementHandler,然后调用它的query方法。打开configuration.newStatementHandler,我们发现生成的是RoutingStatementHandler对象。源代码如下:

public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
    StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
    statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
    return statementHandler;
  }

所以SimpleExecutor类doQuery()方法最后一行执行的query方法是执行的我们发现生成的是RoutingStatementHandler看的query方法。它的源代码如下:

public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
    return delegate.query(statement, resultHandler);
  }

delegate代理的是哪个StatementHandler对象呢?这个对象在RoutingStatementHandler构造方法中赋值的。

public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {

    switch (ms.getStatementType()) {
      case STATEMENT:
        delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
        break;
      case PREPARED:
        delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
        break;
      case CALLABLE:
        delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
        break;
      default:
        throw new ExecutorException("Unknown statement type: " + ms.getStatementType());
    }

  }

ms.getStatementType()在本例中使用的是默认值。MappedStatement的内部类Builder源代码中。

mappedStatement.statementType = StatementType.PREPARED;

所以delegate为PreparedStatementHandler对象,最终调用的也是PreparedStatementHandler下的query方法,源代码如下:

public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
    PreparedStatement ps = (PreparedStatement) statement;
    ps.execute();
    return resultSetHandler.handleResultSets(ps);
  }

逻辑比较清晰,1、执行sql。2、处理返回结果。

原文 

https://segmentfault.com/a/1190000023335937

本站部分文章源于互联网,本着传播知识、有益学习和研究的目的进行的转载,为网友免费提供。如有著作权人或出版方提出异议,本站将立即删除。如果您对文章转载有任何疑问请告之我们,以便我们及时纠正。

PS:推荐一个微信公众号: askHarries 或者qq群:474807195,里面会分享一些资深架构师录制的视频录像:有Spring,MyBatis,Netty源码分析,高并发、高性能、分布式、微服务架构的原理,JVM性能优化这些成为架构师必备的知识体系。还能领取免费的学习资源,目前受益良多

转载请注明原文出处:Harries Blog™ » Mybatis源码分析(六):执行sql

赞 (0)
分享到:更多 ()

评论 0

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址