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

  1. Keep @AutomapConstructor on exactly one constructor and remove it from all others
  2. Alternatively drop the annotations entirely and declare an explicit <constructor> block in the resultMap
  3. 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

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


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