mybatis/mybatis-3 · error · BuilderException

Should be specified either value() or name() attribute in th

Error message

Should be specified either value() or name() attribute in the @CacheNamespaceRef

What it means

Thrown by MapperAnnotationBuilder.parseCacheRef() when @CacheNamespaceRef is present but both attributes are unset: value() defaults to void.class and name() defaults to "". The annotation must say which namespace to borrow the cache from, via either the mapper class (value) or a string namespace (name).

Source

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

  private Properties convertToProperties(Property[] properties) {
    if (properties.length == 0) {
      return null;
    }
    Properties props = new Properties();
    for (Property property : properties) {
      props.setProperty(property.name(), PropertyParser.parse(property.value(), configuration.getVariables()));
    }
    return props;
  }

  private void parseCacheRef() {
    CacheNamespaceRef cacheDomainRef = type.getAnnotation(CacheNamespaceRef.class);
    if (cacheDomainRef != null) {
      Class<?> refType = cacheDomainRef.value();
      String refName = cacheDomainRef.name();
      if (refType == void.class && refName.isEmpty()) {
        throw new BuilderException("Should be specified either value() or name() attribute in the @CacheNamespaceRef");
      }
      if (refType != void.class && !refName.isEmpty()) {
        throw new BuilderException("Cannot use both value() and name() attribute in the @CacheNamespaceRef");
      }
      String namespace = refType != void.class ? refType.getName() : refName;
      try {
        assistant.useCacheRef(namespace);
      } catch (IncompleteElementException e) {
        configuration.addIncompleteCacheRef(new CacheRefResolver(assistant, namespace));
      }
    }
  }

  private String parseResultMap(Method method) {
    Class<?> returnType = getReturnType(method, type);
    Arg[] args = method.getAnnotationsByType(Arg.class);
    Result[] results = method.getAnnotationsByType(Result.class);
    TypeDiscriminator typeDiscriminator = method.getAnnotation(TypeDiscriminator.class);

View on GitHub (pinned to 008069adb1)

Solutions

  1. Point the ref at the cache-owning mapper: @CacheNamespaceRef(UserMapper.class)
  2. Or use the string form: @CacheNamespaceRef(name = "com.acme.UserMapper")
  3. Confirm the referenced class declares @CacheNamespace (a ref to a mapper without a cache fails later with the incomplete-cache error)

Example fix

// before
@CacheNamespaceRef
public interface OrderMapper { ... }
// after
@CacheNamespaceRef(UserMapper.class)
public interface OrderMapper { ... }
Defensive patterns

Strategy: type-guard

Type guard

// Compile-time guard: a wrapper annotation forcing an attribute
@Retention(RetentionPolicy.SOURCE)
public @interface CacheRefTarget {
  Class<?> value() default void.class;
  String name() default "";
}
// runtime check before SqlSessionFactory build:
for (Class<?> mapper : scannedMappers) {
  CacheNamespaceRef r = mapper.getAnnotation(CacheNamespaceRef.class);
  if (r != null && r.value() == void.class && r.name().isEmpty()) {
    throw new IllegalStateException(mapper + " has @CacheNamespaceRef without value/name");
  }
}

Prevention

When it happens

Trigger: Writing @CacheNamespaceRef with no attributes on a mapper interface; defaults (void.class, empty string) fail the check.

Common situations: Assuming @CacheNamespaceRef works standalone like @CacheNamespace does; forgetting the target after refactoring; IDE auto-import picking the annotation without suggesting required attributes.

Related errors


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