ibatis infinite loop when getFirstResultSet

Tue 08 June 2010
  • 手艺 tags:
  • ibatis
  • java published: true comments: true

前几天上线后老大发现几台负载非常高,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
[cc lang="java"]
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;
}

[/cc]
moreResults恒为真,程序出现死循环。

在mybatis 2.5的代码里,这部分已经修改为:
[cc lang="java"]
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();
}
[/cc]
这部分条件判断实在很极致了。

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

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