mybatis/mybatis-3 · error · IllegalStateException

Cannot define both nestedQueryId and nestedResultMapId in pr

Error message

Cannot define both nestedQueryId and nestedResultMapId in property {}

What it means

ResultMapping.Builder.validate() throws IllegalStateException when a single property mapping declares both a nested select (select=) and a nested resultMap (resultMap=). MyBatis cannot load a collection/association both by executing another statement and by nested result-map mapping at the same time (Issue #697), so the combination is rejected at build time.

Source

Thrown at src/main/java/org/apache/ibatis/mapping/ResultMapping.java:163

    }

    public Builder lazy(boolean lazy) {
      resultMapping.lazy = lazy;
      return this;
    }

    public ResultMapping build() {
      // lock down collections
      resultMapping.flags = List.copyOf(resultMapping.flags);
      resultMapping.composites = List.copyOf(resultMapping.composites);
      validate();
      return resultMapping;
    }

    private void validate() {
      // Issue #697: cannot define both nestedQueryId and nestedResultMapId
      if (resultMapping.nestedQueryId != null && resultMapping.nestedResultMapId != null) {
        throw new IllegalStateException(
            "Cannot define both nestedQueryId and nestedResultMapId in property " + resultMapping.property);
      }
      // Issue #4 and GH #39: column is optional only in nested resultMaps but not in the rest
      if (resultMapping.nestedResultMapId == null && resultMapping.column == null
          && resultMapping.composites.isEmpty()) {
        throw new IllegalStateException("Mapping is missing column attribute for property " + resultMapping.property);
      }
      if (resultMapping.getResultSet() != null) {
        int numColumns = 0;
        if (resultMapping.column != null) {
          numColumns = resultMapping.column.split(",").length;
        }
        int numForeignColumns = 0;
        if (resultMapping.foreignColumn != null) {
          numForeignColumns = resultMapping.foreignColumn.split(",").length;
        }
        if (numColumns != numForeignColumns) {
          throw new IllegalStateException(

View on GitHub (pinned to 008069adb1)

Solutions

  1. Choose one strategy per mapping: keep select= for lazy/nested-select loading, or keep resultMap= for join-based nested mapping, and delete the other attribute.
  2. If you need both lazy and joined behavior, define two separate resultMaps (one per strategy) and switch at the statement level.
  3. For join-based collections, remember column= is still required and the nested resultMap must map the child columns.

Example fix

<!-- before -->
<resultMap id="orderMap" type="Order">
  <collection property="items" column="order_id"
              select="loadItems" resultMap="itemMap"/>
</resultMap>

<!-- after (nested resultMap / join fetch) -->
<resultMap id="orderMap" type="Order">
  <collection property="items" resultMap="itemMap"/>
</resultMap>
<!-- or, for lazy loading: <collection property="items" column="order_id" select="loadItems"/> -->
Defensive patterns

Strategy: validation

Prevention

When it happens

Trigger: XML like <collection property="items" column="order_id" select="loadItems" resultMap="itemResultMap"/> — one mapping element carrying both attributes; same for <association>. Programmatic equivalent: ResultMapping.Builder with both nestedQueryId and nestedResultMapId set.

Common situations: Refactoring a lazy-loaded collection (select=) to a joined-fetch resultMap and forgetting to delete the select attribute; copy-paste between a nested-select mapping and a join mapping; merging mapper fragments during cleanup.

Related errors


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