mybatis/mybatis-3 · error · ExecutorException
'argNameBasedConstructorAutoMapping' is enabled and the clas
Error message
'argNameBasedConstructorAutoMapping' is enabled and the class ''{0}'' has multiple constructors, so @AutomapConstructor must be added to one of the constructors. What it means
Thrown when the configuration setting 'argNameBasedConstructorAutoMapping' is enabled and MyBatis needs to auto-map a result type by constructor, but the class declares more than one constructor and none is annotated with @AutomapConstructor. Because argument-name-based matching cannot decide which constructor to use, MyBatis refuses to guess. The annotated-constructor check happens first; only when no constructor carries the annotation does this exception fire.
Source
Thrown at src/main/java/org/apache/ibatis/executor/resultset/DefaultResultSetHandler.java:874
"No constructor found in " + resultType.getName() + " matching " + rsw.getClassNames())));
}
private Optional<Constructor<?>> findConstructorForAutomapping(final Class<?> resultType, ResultSetWrapper rsw) {
Constructor<?>[] constructors = resultType.getDeclaredConstructors();
if (constructors.length == 1) {
return Optional.of(constructors[0]);
}
Optional<Constructor<?>> annotated = Arrays.stream(constructors)
.filter(x -> x.isAnnotationPresent(AutomapConstructor.class)).reduce((x, y) -> {
throw new ExecutorException("@AutomapConstructor should be used in only one constructor.");
});
if (annotated.isPresent()) {
return annotated;
}
if (configuration.isArgNameBasedConstructorAutoMapping()) {
// Finding-best-match type implementation is possible,
// but using @AutomapConstructor seems sufficient.
throw new ExecutorException(MessageFormat.format(
"'argNameBasedConstructorAutoMapping' is enabled and the class ''{0}'' has multiple constructors, so @AutomapConstructor must be added to one of the constructors.",
resultType.getName()));
} else {
return Arrays.stream(constructors).filter(x -> findUsableConstructorByArgTypes(x, rsw.getJdbcTypes())).findAny();
}
}
private boolean findUsableConstructorByArgTypes(final Constructor<?> constructor, final List<JdbcType> jdbcTypes) {
final Class<?>[] parameterTypes = constructor.getParameterTypes();
if (parameterTypes.length != jdbcTypes.size()) {
return false;
}
for (int i = 0; i < parameterTypes.length; i++) {
if (!typeHandlerRegistry.hasTypeHandler(parameterTypes[i], jdbcTypes.get(i))) {
return false;
}
}
return true;View on GitHub (pinned to 008069adb1)
Solutions
- Add org.apache.ibatis.annotations.@AutomapConstructor to exactly one constructor of the result class (the one MyBatis should use for auto-mapping).
- Alternatively remove constructors so the class has exactly one constructor, letting MyBatis pick constructors[0].
- Alternatively disable argNameBasedConstructorAutoMapping so MyBatis falls back to matching constructors by JDBC type arity (findUsableConstructorByArgTypes).
- Alternatively specify the constructor explicitly in the resultMap with a <constructor> node instead of relying on auto-mapping.
Example fix
// before
public class User {
public User() {}
public User(Long id, String name) { ... }
}
// mybatis-config.xml: <setting name="argNameBasedConstructorAutoMapping" value="true"/>
// after
import org.apache.ibatis.annotations.AutomapConstructor;
public class User {
public User() {}
@AutomapConstructor
public User(Long id, String name) { ... }
} Defensive patterns
Strategy: validation
Validate before calling
// at startup, verify every constructor-mapped result type is unambiguous
private static final Predicate<Class<?>> HAS_AUTOMAP =
c -> Arrays.stream(c.getDeclaredConstructors())
.anyMatch(x -> x.isAnnotationPresent(AutomapConstructor.class));
void checkConstructorAutomapping(Configuration cfg, Class<?>... resultTypes) {
if (!cfg.isArgNameBasedConstructorAutoMapping()) return;
for (Class<?> t : resultTypes) {
Constructor<?>[] ctors = t.getDeclaredConstructors();
if (ctors.length > 1 && !HAS_AUTOMAP.test(t)) {
throw new IllegalStateException(t.getName()
+ " has multiple constructors but no @AutomapConstructor;"
+ " argNameBasedConstructorAutoMapping will fail at query time.");
}
}
} Prevention
- Add @AutomapConstructor whenever a result type has more than one constructor and argNameBasedConstructorAutoMapping is on.
- Prefer single-constructor immutable DTOs for auto-mapped results.
- Run a startup smoke test that executes one query per constructor-mapped result type so config errors fail fast at boot, not in production.
When it happens
Trigger: configuration.setArgNameBasedConstructorAutoMapping(true) (or <settings argNameBasedConstructorAutoMapping="true"/>) plus a result type with multiple constructors (e.g. a class with both an all-args and a no-args or partial constructor) mapped via constructor auto-mapping (no explicit <constructor> node, no @AutomapConstructor anywhere). Also requires the class not to have exactly one constructor (constructors[0] shortcut only applies to a single-constructor class).
Common situations: Enabling argNameBasedConstructorAutoMapping to use -parameters javac flag with Java 8+ parameter names, then mapping DTOs that legitimately overload constructors (Lombok @AllArgsConstructor + @NoArgsConstructor). Also hits after adding a convenience/telescoping constructor to a previously single-constructor DTO.
Related errors
- 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}
- Error creating instance. Cause: {cause}
AI-assisted analysis of mybatis/mybatis-3@008069adb1 (2026-08-14).
Data as JSON: /api/errors/be93a5238b6cbe83.
Report an issue: GitHub.