mybatis/mybatis-3 · error · ExecutorException

Cannot construct objects with collections in constructors us

Error message

Cannot construct objects with collections in constructors using multiple result sets yet!

What it means

MyBatis cannot build objects whose constructor takes a collection when the collection rows come from a separate result set (multi-result-set mapping, i.e. a ResultMapping with a foreign nested resultMap driven by another resultSet). This is a hard, documented engine limitation: pending constructor creations across multiple result sets are not implemented, so verifyPendingCreationPreconditions throws immediately when parentMapping != null.

Source

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

      metaObject.setValue(resultMapping.getProperty(), null);

      // create new collection property
      collectionProperty = instantiateCollectionPropertyIfAppropriate(resultMapping, metaObject);
      final MetaObject targetMetaObject = configuration.newMetaObject(collectionProperty);

      // create the pending objects
      for (Object pendingCreation : pendingCreations) {
        if (pendingCreation instanceof PendingConstructorCreation) {
          final PendingConstructorCreation pendingConstructorCreation = (PendingConstructorCreation) pendingCreation;
          targetMetaObject.add(pendingConstructorCreation.create(objectFactory));
        }
      }
    }
  }

  private void verifyPendingCreationPreconditions(ResultMapping parentMapping) {
    if (parentMapping != null) {
      throw new ExecutorException(
          "Cannot construct objects with collections in constructors using multiple result sets yet!");
    }

    if (!mappedStatement.isResultOrdered()) {
      throw new ExecutorException("Cannot reliably construct result if we are not sure the results are ordered "
          + "so that no new previous rows would occur, set resultOrdered on your mapped statement if you have verified this");
    }
  }

  private void createAndStorePendingCreation(ResultHandler<?> resultHandler, ResultSet resultSet,
      DefaultResultContext<Object> resultContext, PendingConstructorCreation pendingCreation) throws SQLException {
    final Object result = pendingCreation.create(objectFactory);
    storeObject(resultHandler, resultContext, result, null, resultSet);
    nestedResultObjects.clear();
  }

  //
  // NESTED RESULT MAP (JOIN MAPPING)

View on GitHub (pinned to 008069adb1)

Solutions

  1. Consolidate to a single result set with a JOIN and map the collection via nested resultMaps (not multiple resultSets).
  2. Keep multiple result sets but map the collection into a setter/setter-based collection property instead of a constructor argument.
  3. Materialize children in application code: fetch both result sets separately and construct the parent manually.
  4. Track MyBatis releases — this limitation is long-standing; verify against the current version before designing around it.

Example fix

// before (unsupported)
<resultMap id="parentMap" type="Parent">
  <constructor>
    <arg property="children" javaType="list" resultMap="childMap" resultSet="childrenRs" column="id"/>
  </constructor>
</resultMap>

// after (supported): single joined result set, nested collection in constructor
<resultMap id="parentMap" type="Parent">
  <constructor>
    <idArg column="id" javaType="long"/>
    <arg property="children" javaType="list" resultMap="childMap"/>
  </constructor>
</resultMap>
Defensive patterns

Strategy: validation

Validate before calling

// reject the unsupported combination at startup instead of query time
for (MappedStatement ms : configuration.getMappedStatements()) {
  if (ms instanceof MappedStatement real
      && real.getResultMaps().stream().anyMatch(rm -> rm.getResultMappings().stream()
          .anyMatch(rm2 -> rm2.getNestedResultMapId() != null && rm2.getResultSet() != null))) {
    throw new IllegalStateException(ms.getId()
        + " combines multi-resultSet mappings with nested results;"
        + " constructor collections across result sets are unsupported.");
  }
}

Prevention

When it happens

Trigger: A statement returning multiple result sets (stored procedure or multi-select) where a resultMap maps one result set's rows into a constructor collection argument of an object built from another result set — the constructor mapping's parentMapping is non-null (rows arrive tagged with an outer parent mapping).

Common situations: Stored procedures returning master rows plus detail rows, wanting an immutable parent whose constructor accepts List<Child>; migrating setter-based collection mappings to constructor mappings while keeping the multi-resultSet design.

Related errors


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