{"record":{"id":"be93a5238b6cbe83","repo":"mybatis/mybatis-3","slug":"argnamebasedconstructorautomapping-is-enabled-an","errorCode":null,"errorMessage":"'argNameBasedConstructorAutoMapping' is enabled and the class ''{0}'' has multiple constructors, so @AutomapConstructor must be added to one of the constructors.","messagePattern":"'argNameBasedConstructorAutoMapping' is enabled and the class ''(.+?)'' has multiple constructors, so @AutomapConstructor must be added to one of the constructors\\.","errorType":"exception","errorClass":"ExecutorException","httpStatus":null,"severity":"error","filePath":"src/main/java/org/apache/ibatis/executor/resultset/DefaultResultSetHandler.java","lineNumber":874,"sourceCode":"            \"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()) {\n      return false;\n    }\n    for (int i = 0; i < parameterTypes.length; i++) {\n      if (!typeHandlerRegistry.hasTypeHandler(parameterTypes[i], jdbcTypes.get(i))) {\n        return false;\n      }\n    }\n    return true;","sourceCodeStart":856,"sourceCodeEnd":892,"githubUrl":"https://github.com/mybatis/mybatis-3/blob/008069adb1b089579b5dcba87ee591908b263274/src/main/java/org/apache/ibatis/executor/resultset/DefaultResultSetHandler.java#L856-L892","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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."],"exampleFix":"// before\npublic class User {\n  public User() {}\n  public User(Long id, String name) { ... }\n}\n// mybatis-config.xml: <setting name=\"argNameBasedConstructorAutoMapping\" value=\"true\"/>\n\n// after\nimport org.apache.ibatis.annotations.AutomapConstructor;\npublic class User {\n  public User() {}\n  @AutomapConstructor\n  public User(Long id, String name) { ... }\n}","handlingStrategy":"validation","validationCode":"// at startup, verify every constructor-mapped result type is unambiguous\nprivate static final Predicate<Class<?>> HAS_AUTOMAP =\n    c -> Arrays.stream(c.getDeclaredConstructors())\n               .anyMatch(x -> x.isAnnotationPresent(AutomapConstructor.class));\n\nvoid checkConstructorAutomapping(Configuration cfg, Class<?>... resultTypes) {\n  if (!cfg.isArgNameBasedConstructorAutoMapping()) return;\n  for (Class<?> t : resultTypes) {\n    Constructor<?>[] ctors = t.getDeclaredConstructors();\n    if (ctors.length > 1 && !HAS_AUTOMAP.test(t)) {\n      throw new IllegalStateException(t.getName()\n          + \" has multiple constructors but no @AutomapConstructor;\"\n          + \" argNameBasedConstructorAutoMapping will fail at query time.\");\n    }\n  }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["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."],"tags":["mybatis","constructor-automapping","configuration","annotations"],"backgroundTag":null,"analyzedSha":"008069adb1b089579b5dcba87ee591908b263274","analyzedAt":"2026-08-14T13:07:10.264Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}