ibatis 分页实现原理

项目中要到了ibatis 发现分页性能低下,所以看了一下源码,不禁一个寒战,下面发出来简单分析一下,如有错处还请指定。。
下面看ibatis最终查询方法
  /**
   *
   * @param statementScope  statement对象
   * @param rs    数据集合
   * @param skipResults  数据开始位置
   * @param maxResults  数据结束位置
   * @param callback   数据返回(封装)对象
   * @throws SQLException
   */
   private void handleResults(StatementScope statementScope, ResultSet rs, int skipResults, int maxResults, RowHandlerCallback callback) throws SQLException {
      try {
        statementScope.setResultSet(rs);
        ResultMap resultMap = statementScope.getResultMap();
        if (resultMap != null) {
          // Skip Results
          if (rs.getType() != ResultSet.TYPE_FORWARD_ONLY) {
            if (skipResults > 0) {
              rs.absolute(skipResults);
            }
          } else {
            for (int i = 0; i < skipResults; i++) {
              if (!rs.next()) {
                return;
              }
            }
          }

          // Get Results
          int resultsFetched = 0;
          while ((maxResults == SqlExecutor.NO_MAXIMUM_RESULTS || resultsFetched < maxResults) && rs.next()) {
            Object[] columnValues = resultMap.resolveSubMap(statementScope, rs).getResults(statementScope, rs);
            callback.handleResultObject(statementScope, columnValues, rs);
            resultsFetched++;
          }
        }
      } finally {
        statementScope.setResultSet(null);
      }
    }
}

statementScope对象获取方式,ID为字符串参数,原来是ibatis的sessionscope中启动一个statement
MappedStatement ms = getMappedStatement(id);
StatementScope statementScope = beginStatementScope(sessionScope, ms);


再看rs如何来的,如下
ResultSet rs;
rs = getFirstResultSet(statementScope, ps);

再看getFirstResultSet获取记录集的方法,而getResultSet方法确实获取当前statement的所有数据。
private ResultSet getFirstResultSet(StatementScope scope, Statement stmt) throws SQLException {
    ResultSet rs = null;
    boolean hasMoreResults = true;
    while (hasMoreResults) {
      rs = stmt.getResultSet();
      if (rs != null) {
        break;
      }
      hasMoreResults = moveToNextResultsIfPresent(scope, stmt);
    }
    return rs;
  }
看到这里有些失望,难道记录集是这样获取的。。。。


再回头看handleResults方法,rs.absolute(skipResults); 设定游标点,超大记录集时候这个方法显得和愚笨!!
再看更有意思的方法:是检测数据是否有效性的。
for (int i = 0; i < skipResults; i++) {
    if (!rs.next()) {
      return;
    }
  }
如果开始点是100亿呢,那么循环这个次数,我不说性能了,大家也能看出来。睡个觉再来看结果来吧。如果能成功,下一条rs.next()继续循环去吧,循环不是错!!
这还不算完这是查询检测数据而已,还没有整理要返回的数据。。。。
while ((maxResults == SqlExecutor.NO_MAXIMUM_RESULTS || resultsFetched < maxResults) && rs.next()) {
    Object[] columnValues = resultMap.resolveSubMap(statementScope, rs).getResults(statementScope, rs);
    callback.handleResultObject(statementScope, columnValues, rs);
    resultsFetched++;
  }
又一次无知的循环。。。如果循环到想要的记录,就吧记录添加到另外一个对象中。
到此获取“数据next分页”已经差不多实现了。。。。

本人亲身做过实验,每页20条记录,第一页1秒(可能网络有消耗)2万页的时候需要20秒,20万页的时候程序直接死掉。实现就这么无情的被 while 掉了。。。。。

猜你喜欢

转载自ayahi.iteye.com/blog/2091902
今日推荐