ibatis infinite loop when getFirstResultSet

前几天上线后老大发现几台负载非常高,dump线程状态后发现多个线程死循环在同一处,于是发现了ibatis的这个bug:

https://issues.apache.org/jira/browse/IBATIS-384

https://issues.apache.org/jira/browse/IBATIS-587

在mysql数据库上,没有结果集时,stmt.getUpdateCount()会返回0,而非-1.
ibatis 2.4.3

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

  private boolean moveToNextResultsIfPresent(StatementScope scope, Statement stmt) throws SQLException {
    boolean moreResults;
    // This is the messed up JDBC approach for determining if there are more results
    moreResults = !(((moveToNextResultsSafely(scope, stmt) == false) && (stmt.getUpdateCount() == -1)));
    return moreResults;
  }

  private boolean moveToNextResultsSafely(StatementScope scope, Statement stmt) throws SQLException {
    if (forceMultipleResultSetSupport(scope) || stmt.getConnection().getMetaData().supportsMultipleResultSets()) {
      return stmt.getMoreResults();
    }
    return false;
  }

moreResults恒为真,程序出现死循环。

在mybatis 2.5的代码里,这部分已经修改为:

  private boolean moveToNextResultsIfPresent(StatementScope scope, Statement stmt) throws SQLException {
    boolean moreResults;
    // This is the messed up JDBC approach for determining if there are more results
    boolean movedToNextResultsSafely = moveToNextResultsSafely(scope, stmt);
    int updateCount = stmt.getUpdateCount();

    moreResults = !(!movedToNextResultsSafely && (updateCount == -1));

    //ibatis-384: workaround for mysql not returning -1 for stmt.getUpdateCount()
    if (moreResults == true){
        moreResults = !(!movedToNextResultsSafely && !isMultipleResultSetSupportPresent(scope, stmt));
    }

    return moreResults;
  }

  private boolean moveToNextResultsSafely(StatementScope scope, Statement stmt) throws SQLException {
    if (isMultipleResultSetSupportPresent(scope, stmt)) {
      return stmt.getMoreResults();
    }
    return false;
  }

  /**
   * checks whether multiple result set support is present - either by direct support of the database driver or by forcing it
   */

  private boolean isMultipleResultSetSupportPresent(StatementScope scope,
          Statement stmt) throws SQLException {
      return forceMultipleResultSetSupport(scope) || stmt.getConnection().getMetaData().supportsMultipleResultSets();
  }

这部分条件判断实在很极致了。

新的实现当getUpdateResult是0时,moreResults恒为真,这时再进行一个判断,如果是由于isMultipleResultSetSupportPresent为false导致了moveToNextResultsSafely为false,那么实际moreResults应是false

MyBatis 2.5还有一个issue没有解决,离发布还有一些时间,这个问题只好签出新版本代码自己build了

4 thoughts on “ibatis infinite loop when getFirstResultSet

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>