{"record":{"id":"361c4d131a1f5f3e","repo":"mybatis/mybatis-3","slug":"automapconstructor-should-be-used-in-only-one-con","errorCode":null,"errorMessage":"@AutomapConstructor should be used in only one constructor.","messagePattern":"@AutomapConstructor should be used in only one constructor\\.","errorType":"exception","errorClass":"ExecutorException","httpStatus":null,"severity":"error","filePath":"src/main/java/org/apache/ibatis/executor/resultset/DefaultResultSetHandler.java","lineNumber":866,"sourceCode":"\n    return objectFactory.create(resultType, constructorArgTypes, constructorArgs);\n  }\n\n  private Object createByConstructorSignature(ResultSetWrapper rsw, ResultMap resultMap, String columnPrefix,\n      Class<?> resultType, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) throws SQLException {\n    return applyConstructorAutomapping(rsw, resultMap, columnPrefix, resultType, constructorArgTypes, constructorArgs,\n        findConstructorForAutomapping(resultType, rsw).orElseThrow(() -> new ExecutorException(\n            \"No constructor found in \" + resultType.getName() + \" matching \" + rsw.getClassNames())));\n  }\n\n  private Optional<Constructor<?>> findConstructorForAutomapping(final Class<?> resultType, ResultSetWrapper rsw) {\n    Constructor<?>[] constructors = resultType.getDeclaredConstructors();\n    if (constructors.length == 1) {\n      return Optional.of(constructors[0]);\n    }\n    Optional<Constructor<?>> annotated = Arrays.stream(constructors)\n        .filter(x -> x.isAnnotationPresent(AutomapConstructor.class)).reduce((x, y) -> {\n          throw new ExecutorException(\"@AutomapConstructor should be used in only one constructor.\");\n        });\n    if (annotated.isPresent()) {\n      return annotated;\n    }\n    if (configuration.isArgNameBasedConstructorAutoMapping()) {\n      // Finding-best-match type implementation is possible,\n      // but using @AutomapConstructor seems sufficient.\n      throw new ExecutorException(MessageFormat.format(\n          \"'argNameBasedConstructorAutoMapping' is enabled and the class ''{0}'' has multiple constructors, so @AutomapConstructor must be added to one of the constructors.\",\n          resultType.getName()));\n    } else {\n      return Arrays.stream(constructors).filter(x -> findUsableConstructorByArgTypes(x, rsw.getJdbcTypes())).findAny();\n    }\n  }\n\n  private boolean findUsableConstructorByArgTypes(final Constructor<?> constructor, final List<JdbcType> jdbcTypes) {\n    final Class<?>[] parameterTypes = constructor.getParameterTypes();\n    if (parameterTypes.length != jdbcTypes.size()) {","sourceCodeStart":848,"sourceCodeEnd":884,"githubUrl":"https://github.com/mybatis/mybatis-3/blob/008069adb1b089579b5dcba87ee591908b263274/src/main/java/org/apache/ibatis/executor/resultset/DefaultResultSetHandler.java#L848-L884","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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"],"exampleFix":"// before\npublic class User {\n  @AutomapConstructor public User(Long id, String name) { ... }\n  @AutomapConstructor public User(Long id) { ... }        // ambiguous\n}\n// after\npublic class User {\n  public User(Long id, String name) { ... }              // annotated one only\n  public User(Long id) { ... }\n}","handlingStrategy":"validation","validationCode":"long n = Arrays.stream(User.class.getDeclaredConstructors())\n    .filter(c -> c.isAnnotationPresent(AutomapConstructor.class)).count();\nif (n > 1) throw new IllegalStateException(\"@AutomapConstructor allowed on only one constructor of \" + User.class);","typeGuard":"static boolean singleAutomapConstructor(Class<?> c) { return Arrays.stream(c.getDeclaredConstructors()).filter(x -> x.isAnnotationPresent(AutomapConstructor.class)).count() <= 1; }","tryCatchPattern":null,"preventionTips":["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"],"tags":["mybatis","automapping","constructor","annotation"],"backgroundTag":null,"analyzedSha":"008069adb1b089579b5dcba87ee591908b263274","analyzedAt":"2026-08-14T13:07:10.264Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}