mybatis/mybatis-3 · error · ReflectionException
Cannot add a collection result to non-collection based resul
Error message
Cannot add a collection result to non-collection based resultMapping: {} What it means
Thrown by PendingConstructorCreation when a constructor mapping is expected to receive a collection but the mapped constructor parameter's Java type is not a collection (objectFactory.isCollection(parameterType) is false). MyBatis cannot pour multiple nested rows into a non-collection constructor argument, so it fails with the offending resultMapping string.
Source
Thrown at src/main/java/org/apache/ibatis/executor/resultset/PendingConstructorCreation.java:63
PendingConstructorCreation(Class<?> resultType, List<Class<?>> types, List<Object> args) {
// since all our keys are based on result map id, we know we will never go over args size
final int maxSize = types.size();
this.linkedCollectionMetaInfo = new HashMap<>(maxSize);
this.linkedCollectionsByKey = new HashMap<>(maxSize);
this.linkedCreationsByKey = new HashMap<>(maxSize);
this.resultType = resultType;
this.constructorArgTypes = types;
this.constructorArgs = args;
}
@SuppressWarnings("unchecked")
Collection<Object> initializeCollectionForResultMapping(ObjectFactory objectFactory, ResultMap resultMap,
ResultMapping constructorMapping, Integer index) {
final Class<?> parameterType = constructorMapping.getJavaType();
if (!objectFactory.isCollection(parameterType)) {
throw new ReflectionException(
"Cannot add a collection result to non-collection based resultMapping: " + constructorMapping);
}
return linkedCollectionsByKey.computeIfAbsent(new PendingCreationKey(constructorMapping), k -> {
// this will allow us to verify the types of the collection before creating the final object
linkedCollectionMetaInfo.put(index, new PendingCreationMetaInfo(resultMap.getType(), k));
// will be checked before we finally create the object) as we cannot reliably do that here
return (Collection<Object>) objectFactory.create(parameterType);
});
}
void linkCreation(ResultMapping constructorMapping, PendingConstructorCreation pcc) {
final PendingCreationKey creationKey = new PendingCreationKey(constructorMapping);
final List<PendingConstructorCreation> pendingConstructorCreations = linkedCreationsByKey
.computeIfAbsent(creationKey, k -> new ArrayList<>());
if (pendingConstructorCreations.contains(pcc)) {View on GitHub (pinned to 008069adb1)
Solutions
- Change the constructor parameter to a collection type (List<Item>, Collection<Item>, or a concrete class) matching the nested rows.
- Or set javaType on the <arg> to a collection type (e.g. javaType="java.util.ArrayList") so the mapping declares the collection.
- If only one child is expected, use an <association> nested in the constructor instead of a collection mapping.
- Keep setter-based mapping for that property if the constructor must keep a singular parameter.
Example fix
// before
public Order(Long id, Item item) { ... } // mapped as collection arg
<constructor><arg property="items" resultMap="itemMap" javaType="item"/></constructor>
// after
public Order(Long id, List<Item> items) { ... }
<constructor><arg property="items" resultMap="itemMap" javaType="java.util.ArrayList"/></constructor> Defensive patterns
Strategy: validation
Validate before calling
// verify each constructor arg mapped as a collection is a collection type
for (ResultMapping rm : resultMap.getConstructorResultMappings()) {
if (rm.getNestedResultMapId() != null && rm.getJavaType() != null
&& !Collection.class.isAssignableFrom(rm.getJavaType())) {
throw new IllegalStateException(rm.getProperty() + " is collection-mapped but javaType "
+ rm.getJavaType() + " is not a collection");
}
} Type guard
static boolean isCollectionArg(ResultMapping rm) {
return rm.getNestedQueryId() == null && rm.getNestedResultMapId() != null
&& rm.getJavaType() != null && Collection.class.isAssignableFrom(rm.getJavaType());
} Prevention
- When moving collection properties from setters to constructor args, change the parameter type to List/Set in the same commit.
- Add explicit javaType on constructor <arg> elements so mapping intent is machine-checkable.
When it happens
Trigger: A <constructor><arg property="..." resultMap="..."/></constructor> style mapping (constructor collection mapping) where the corresponding constructor parameter is a single object type (e.g. Item rather than List<Item>), or javaType on the arg is wrong.
Common situations: Refactoring a setter collection property into a constructor without changing the parameter from singular to List; copy-pasting an <arg> and forgetting javaType="list" or a concrete collection type; generics erased so MyBatis sees the raw parameter type incorrectly after signature changes.
Related errors
- Error in result map '{resultMapId}'. Failed to find a constr
- Error in result map '{resultMapId}'. We do not support parti
- Do not know how to create an instance of " + resultType
- Error getting constructor collection nested result map value
- Cannot construct objects with collections in constructors us
AI-assisted analysis of mybatis/mybatis-3@008069adb1 (2026-08-14).
Data as JSON: /api/errors/3b87fa8ed3fea575.
Report an issue: GitHub.