Mybatis源码 select分析

来源:互联网 发布:显卡品牌 知乎 编辑:程序博客网 时间:2024/05/30 23:44

在我们上一篇的分析中分析了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