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
- 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.
- If you need both lazy and joined behavior, define two separate resultMaps (one per strategy) and switch at the statement level.
- 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
- Rule of thumb: select= OR resultMap= on a collection/association, never both.
- When refactoring from lazy select= to join-based resultMap=, delete the select attribute in the same commit.
- Cover both mapping styles with integration tests so attribute conflicts fail in CI, not at runtime.
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
- ResultMaps must have an id
- Invalid bound statement (not found): {mapperInterface}.{meth
- Type {type} is not known to the MapperRegistry.
- Error resolving JdbcType. Cause: {cause}
- Error resolving ResultSetType. Cause: {cause}
AI-assisted analysis of mybatis/mybatis-3@008069adb1 (2026-08-14).
Data as JSON: /api/errors/7067930009ec70a0.
Report an issue: GitHub.