mybatis/mybatis-3 · error · CacheException

Cache instances require an ID.

Error message

Cache instances require an ID.

What it means

Thrown by PerpetualCache.equals() when the cache's id is null. Cache identity in mybatis is its id (namespace); equals() refuses to compare a cache that never got one. In practice ids are assigned by the builder (MapperBuilderAssistant.useNewCache passes the namespace), so a null id means a cache was constructed directly via new PerpetualCache(null) — almost always in custom cache code or tests.

Source

Thrown at src/main/java/org/apache/ibatis/cache/impl/PerpetualCache.java:70

  @Override
  public Object getObject(Object key) {
    return cache.get(key);
  }

  @Override
  public Object removeObject(Object key) {
    return cache.remove(key);
  }

  @Override
  public void clear() {
    cache.clear();
  }

  @Override
  public boolean equals(Object o) {
    if (getId() == null) {
      throw new CacheException("Cache instances require an ID.");
    }
    if (this == o) {
      return true;
    }
    if (!(o instanceof Cache)) {
      return false;
    }

    Cache otherCache = (Cache) o;
    return getId().equals(otherCache.getId());
  }

  @Override
  public int hashCode() {
    if (getId() == null) {
      throw new CacheException("Cache instances require an ID.");
    }
    return getId().hashCode();

View on GitHub (pinned to 008069adb1)

Solutions

  1. Always construct caches with a non-null id: new PerpetualCache("com.acme.UserMapper")
  2. In custom decorators, delegate getId() to the wrapped cache instead of constructing a fresh id-less one
  3. Assert cache.getId() != null in test fixtures before comparing caches

Example fix

// before
Cache cache = new PerpetualCache(null);
Set<Cache> caches = new HashSet<>(); caches.add(cache);

// after
Cache cache = new PerpetualCache("com.acme.UserMapper");
Set<Cache> caches = new HashSet<>(); caches.add(cache);
Defensive patterns

Strategy: validation

Validate before calling

if (cache.getId() == null) throw new IllegalStateException("cache constructed without an id (namespace)");

Type guard

static boolean hasCacheId(Cache c) { return c != null && c.getId() != null && !c.getId().isEmpty(); }

Prevention

When it happens

Trigger: new PerpetualCache(null) (or a custom Cache with null id) followed by any equals() comparison — putting it in a Set/Map key, configuration.addCache comparisons, or test assertions. Framework-built caches always carry the namespace id, so this is a hand-construction problem.

Common situations: Unit tests constructing caches without ids; custom CacheDecorator wrappers that forward to a delegate created with a null id; copy-pasted cache factory code that forgets the id argument.

Related errors


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