mybatis/mybatis-3 · error · ExecutorException
@AutomapConstructor should be used in only one constructor.
Error message
@AutomapConstructor should be used in only one constructor.
What it means
When a result class has multiple constructors and automapping must choose one, MyBatis looks for constructors annotated @AutomapConstructor. findConstructorForAutomapping uses reduce() over annotated constructors; if two or more carry the annotation, the reducer throws this ExecutorException — the choice would be ambiguous.
Source
Thrown at src/main/java/org/apache/ibatis/executor/resultset/DefaultResultSetHandler.java:866
return objectFactory.create(resultType, constructorArgTypes, constructorArgs);
}
private Object createByConstructorSignature(ResultSetWrapper rsw, ResultMap resultMap, String columnPrefix,
Class<?> resultType, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) throws SQLException {
return applyConstructorAutomapping(rsw, resultMap, columnPrefix, resultType, constructorArgTypes, constructorArgs,
findConstructorForAutomapping(resultType, rsw).orElseThrow(() -> new ExecutorException(
"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()) {View on GitHub (pinned to 008069adb1)
Solutions
- Keep @AutomapConstructor on exactly one constructor and remove it from all others
- Alternatively drop the annotations entirely and declare an explicit <constructor> block in the resultMap
- If the class has exactly one constructor, the annotation is unnecessary — remove all of them
Example fix
// before
public class User {
@AutomapConstructor public User(Long id, String name) { ... }
@AutomapConstructor public User(Long id) { ... } // ambiguous
}
// after
public class User {
public User(Long id, String name) { ... } // annotated one only
public User(Long id) { ... }
} Defensive patterns
Strategy: validation
Validate before calling
long n = Arrays.stream(User.class.getDeclaredConstructors())
.filter(c -> c.isAnnotationPresent(AutomapConstructor.class)).count();
if (n > 1) throw new IllegalStateException("@AutomapConstructor allowed on only one constructor of " + User.class); Type guard
static boolean singleAutomapConstructor(Class<?> c) { return Arrays.stream(c.getDeclaredConstructors()).filter(x -> x.isAnnotationPresent(AutomapConstructor.class)).count() <= 1; } Prevention
- Annotate exactly one constructor per automapped class
- Re-check annotations after adding overloads or Lombok changes
- Prefer explicit <constructor> mappings for classes with many constructors
When it happens
Trigger: resultType class has 2+ declared constructors, more than one annotated with @AutomapConstructor, and the resultMap relies on automatic constructor mapping (no explicit <constructor> block, automapping enabled).
Common situations: Copy-pasting @AutomapConstructor onto a convenience overload; Lombok-generating an extra constructor while the developer also annotated a hand-written one; refactoring value objects and leaving stale annotations on old constructors.
Related errors
- Should be specified either value() or name() attribute in th
- Cannot use both value() and name() attribute in the @CacheNa
- If there is no type discriminator, then the NamedResultMap a
- Cannot use both @One and @Many annotations in the same @Resu
- No @Intercepts annotation was found in interceptor {}
AI-assisted analysis of mybatis/mybatis-3@008069adb1 (2026-08-14).
Data as JSON: /api/errors/361c4d131a1f5f3e.
Report an issue: GitHub.