Mybatis源码 select分析

在我们上一篇的分析中分析了Mybatis一次插入执行流程分析,因为我们所有的插入执行都会转换为update,所以这一次我们执行一次我们的查询,看看他的过程是怎么样的?代码如下

Student student = studentMapper.selectStudentWithAddress(1);

这段代码跟踪,因为他是一个代理我们直接走进代理,和上一篇分析的一样在excute中有一个select的判断:

 else if (SqlCommandType.SELECT == command.getType()) {
      if (method.returnsVoid() && method.hasResultHandler()) {
        executeWithResultHandler(sqlSession, args);
        result = null;
      } else if (method.returnsMany()) {
        result = executeForMany(sqlSession, args);
      } else if (method.returnsMap()) {
        result = executeForMap(sqlSession, args);
      } else {
        Object param = method.convertArgsToSqlCommandParam(args);
        result = sqlSession.selectOne(command.getName(), param);
      }

我们选择进入一个

result = executeForMany(sqlSession, args);

进入executeForMany的代码中:

private <E> Object executeForMany(SqlSession sqlSession, Object[] args) {
    List<E> result;//结果集
    Object param = method.convertArgsToSqlCommandParam(args);//转换参数
    if (method.hasRowBounds()) {//是否有行数限制
      RowBounds rowBounds = method.extractRowBounds(args);
      result = sqlSession.<E>selectList(command.getName(), param, rowBounds);
    } else {
      result = sqlSession.<E>selectList(command.getName(), param);
    }
    // 对结果集进行处理
    if (!method.getReturnType().isAssignableFrom(result.getClass())) {
      if (method.getReturnType().isArray()) {
        return convertToArray(result);
      } else {
        return convertToDeclaredCollection(sqlSession.getConfiguration(), result);
      }
    }
    return result;
  }

上面的代码归纳起来:
1。转换参数
2。判断是否有行数限制,来执行不同的查询。
3。对返回的结果进行一些处理,如果需要数组转换为数组,否则转化为申明的结果集。
我们这里跟踪

result = sqlSession.<E>selectList(command.getName(), param, rowBounds);

继续进入我们的sqlSession的代码:

public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
    try {
      MappedStatement ms = configuration.getMappedStatement(statement);//获取我们的映射Statement
      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.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);这句是我们的核心执行,和以前分析的一样我们要记住我们所有对的真正执行过程都是在executor中执行的。

简单说明一下wrapCollection(parameter),把我们的参数包装为一个map.接下来进入query:

  public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
    BoundSql boundSql = ms.getBoundSql(parameter);//得到真正的sql
    CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);//得到我们之前的缓存Key
    return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
 }

ms.getBoundSql(parameter)这里是我们解析sql的地方上一篇已经说过,这里不便多谈。接下来得到我们的缓存key如果在同一次SqlSession中我们就会缓存查询数据:

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

过程简述为
1。上面的代码有一个int类型的查询栈用来标记当前是否是第一次查询,如果是就清除当前的缓存。
2。在执行查询之前首先查询栈自加1。
3。判断结果集处理器是否为空,如果为空则从缓存中获取,如果不为空则赋值为null。
4。进入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;
  }

核心执行:

list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);

模板方法,我们进入默认的SimpleExecutor.class的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.<E>query(stmt, resultHandler);
    } finally {
      closeStatement(stmt);
    }
  }

和插入一样,这里的Handler是个代理,在执行真正的查询之前我们可以在这里进行分页拦截,后面我也会详细分析如何写一个mybati的分页拦截器。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值