mybatis/mybatis-3 · error · ExecutorException

Cursor results cannot be mapped to multiple resultMaps

Error message

Cursor results cannot be mapped to multiple resultMaps

What it means

DefaultResultSetHandler.handleCursorResultSets only supports a single ResultSet mapping: when a statement run through selectCursor()/Cursor return has more than one resultMap declared (multiple result sets), it throws this ExecutorException because a Cursor is a streaming single-result API.

Source

Thrown at src/main/java/org/apache/ibatis/executor/resultset/DefaultResultSetHandler.java:259

        resultSetCount++;
      }
    }

    return collapseSingleResultList(multipleResults);
  }

  @Override
  public <E> Cursor<E> handleCursorResultSets(Statement stmt) throws SQLException {
    ErrorContext.instance().activity("handling cursor results").object(mappedStatement.getId());

    ResultSetWrapper rsw = getFirstResultSet(stmt);

    List<ResultMap> resultMaps = mappedStatement.getResultMaps();

    int resultMapCount = resultMaps.size();
    validateResultMapsCount(rsw, resultMapCount);
    if (resultMapCount != 1) {
      throw new ExecutorException("Cursor results cannot be mapped to multiple resultMaps");
    }

    ResultMap resultMap = resultMaps.get(0);
    return new DefaultCursor<>(this, resultMap, rsw, rowBounds);
  }

  private ResultSetWrapper getFirstResultSet(Statement stmt) throws SQLException {
    ResultSet rs = null;
    SQLException e1 = null;

    try {
      rs = stmt.getResultSet();
    } catch (SQLException e) {
      // Oracle throws ORA-17283 for implicit cursor
      e1 = e;
    }

    try {

View on GitHub (pinned to 008069adb1)

Solutions

  1. Use selectList/List-returning mapper methods for statements that return multiple result maps
  2. Split the stored procedure or statement so each Cursor maps exactly one result set
  3. If you only need the first result set, define a dedicated statement with a single resultMap for it

Example fix

// before: statement maps 2 resultSets
Cursor<Order> c = session.selectCursor("sel.ordersWithItems"); // throws
// after: dedicated single-resultMap statement
Cursor<Order> c = session.selectCursor("sel.ordersOnly");
List<Item> items = session.selectList("sel.itemsOnly");
Defensive patterns

Strategy: validation

Validate before calling

// Only use Cursor when the statement maps exactly one result set
MappedStatement ms = session.getConfiguration().getMappedStatement("sel.ordersWithItems");
if (ms.getResultMaps().size() != 1) { throw new UnsupportedOperationException("Use selectList for multi-resultMap statements"); }
try (Cursor<Order> c = session.selectCursor("sel.ordersWithItems")) { ... }

Try / catch

try (Cursor<Order> c = session.selectCursor(stmt)) { ... } catch (ExecutorException e) { if (e.getMessage().contains("multiple resultMaps")) { fallback to session.selectList(stmt); } else throw e; }

Prevention

When it happens

Trigger: sqlSession.selectCursor(...) (or a mapper method returning Cursor<T>) on a mapped statement whose resultMaps list size != 1 — e.g. two <resultSet> mappings in a stored-procedure mapper, or resultType plus an extra resultMap after refactoring.

Common situations: Converting a selectList call backed by a multi-resultset stored procedure to selectCursor for streaming; annotating a mapper method with @ResultMap lists yielding multiple maps; copy-pasting a multi-resultset statement and switching its consumer to Cursor.

Related errors


AI-assisted analysis of mybatis/mybatis-3@008069adb1 (2026-08-14). Data as JSON: /api/errors/3e5175d0739e211a. Report an issue: GitHub.