mybatis/mybatis-3 · error · BuilderException

If there is no type discriminator, then the NamedResultMap…

Error message

If there is no type discriminator, then the NamedResultMap annotation requires at least one constructor argument or property mapping

What it means

Thrown by MapperAnnotationBuilder.validateNamedResultMap(): a @NamedResultMap annotation declares neither a type discriminator nor any constructorArguments() nor propertyMappings(). Without at least one mapping (or a discriminator to route types), the named result map would carry no column-to-property information and is rejected as a configuration error.

Solutions

  1. Add at least one @Arg in constructorArguments or @Property in propertyMappings to the @NamedResultMap
  2. Or add a typeDiscriminator so the map routes among subtypes
  3. If you only wanted automapping by resultType, drop @NamedResultMap entirely and use resultType on the statement

Example fix

// before
@NamedResultMap(id = "userMap")
// after
@NamedResultMap(id = "userMap", propertyMappings = @Property(column = "user_name", property = "userName"))
Defensive patterns

Strategy: validation

Validate before calling

for (Method m : mapper.getMethods()) {
  NamedResultMap nrm = m.getAnnotation(NamedResultMap.class);
  if (nrm != null && nrm.constructorArguments().length == 0
      && nrm.propertyMappings().length == 0
      && "@null@".equals(nrm.typeDiscriminator().column())) { /* default sentinel */
    throw new IllegalStateException(m + " has empty @NamedResultMap");
  }
}

Prevention

When it happens

Trigger: @NamedResultMap(id = "userMap") with empty constructorArguments, propertyMappings, and typeDiscriminator defaulting to the NULL discriminator column.

Common situations: Adding @NamedResultMap intending to reuse it via @ResultMap before filling in mappings; misunderstanding that the annotation requires explicit mappings (unlike automapping); leftover stub annotation after removing mappings.

Related errors


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

Appendix: source

Thrown at src/main/java/org/apache/ibatis/builder/annotation/MapperAnnotationBuilder.java:266

    String resultMapId = type.getName() + '.' + namedResultMap.id();
    Class<?> returnType = namedResultMap.javaType();
    Arg[] args = namedResultMap.constructorArguments();
    Result[] results = namedResultMap.propertyMappings();
    TypeDiscriminator typeDiscriminator = namedResultMap.typeDiscriminator();
    if (AnnotationConstants.NULL_TYPE_DISCRIMINATOR.equals(typeDiscriminator.column())) {
      typeDiscriminator = null;
    }

    applyResultMap(resultMapId, returnType, args, results, typeDiscriminator);
  }

  private void validateNamedResultMap(NamedResultMap namedResultMap) {
    if (!AnnotationConstants.NULL_TYPE_DISCRIMINATOR.equals(namedResultMap.typeDiscriminator().column())) {
      return;
    }

    if (namedResultMap.constructorArguments().length == 0 && namedResultMap.propertyMappings().length == 0) {
      throw new BuilderException("If there is no type discriminator, then the NamedResultMap annotation "
          + "requires at least one constructor argument or property mapping");
    }
  }

  private String generateResultMapName(Method method) {
    Results results = method.getAnnotation(Results.class);
    if (results != null && !results.id().isEmpty()) {
      return type.getName() + "." + results.id();
    }
    StringBuilder suffix = new StringBuilder();
    for (Class<?> c : method.getParameterTypes()) {
      suffix.append("-");
      suffix.append(c.getSimpleName());
    }
    if (suffix.length() < 1) {
      suffix.append("-void");
    }
    return type.getName() + "." + method.getName() + suffix;

View on GitHub (pinned to 008069adb1)