hibernate/hibernate-orm · error · UnsupportedOperationException

AnyType not supported as part of cache key!

Error message

AnyType not supported as part of cache key!

What it means

AnyType (the type Hibernate assigns to @Any/@ManyToAny mappings) overrides Type.disassemble(Object, SessionFactoryImplementor) to throw UnsupportedOperationException, because an any-typed value (discriminator + id pair pointing at different entities) has no stable representation usable as a second-level cache key. The two-argument disassemble is invoked by DefaultCacheKeysFactory when materializing entity-id and natural-id cache keys, so the error surfaces the first time a cached entity whose key includes an @Any value is read or written through the cache.

Source

Thrown at hibernate-core/src/main/java/org/hibernate/type/AnyType.java:363

	public Serializable disassemble(Object value, SharedSessionContractImplementor session, Object owner) throws HibernateException {
		if ( value == null ) {
			return null;
		}
		else {
			return new ObjectTypeCacheEntry(
					session.bestGuessEntityName( value ),
					getEntityIdentifierIfNotUnsaved(
							session.bestGuessEntityName( value ),
							value,
							session
					)
			);
		}
	}

	@Override
	public Serializable disassemble(Object value, SessionFactoryImplementor sessionFactory) throws HibernateException {
		throw new UnsupportedOperationException( "AnyType not supported as part of cache key!" );
	}

	@Override
	public Object replace(Object original, Object target, SharedSessionContractImplementor session, Object owner, Map<Object, Object> copyCache)
			throws HibernateException {
		if ( original == null ) {
			return null;
		}
		else {
			final String entityName = session.bestGuessEntityName( original );
			final Object id = getEntityIdentifierIfNotUnsaved( entityName, original, session );
			return session.internalLoad( entityName, id, eager, false );
		}
	}

	// CompositeType implementation ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

	@Override

View on GitHub (pinned to fad1729dce)

Solutions

  1. Remove the @Any attribute from the natural id / composite key and key the cache on a plain value (e.g. the id column alone).
  2. Replace @Any with a concrete @ManyToOne (or several typed associations) if the association must participate in cache keys.
  3. If polymorphism is required, model the reference as a first-class association table (join entity with discriminator + fk) instead of @Any.
  4. Exclude the entity from second-level caching if the @Any must stay in the key position.

Example fix

// before
@Entity
@Cacheable
public class Document {
    @Id Long id;

    @Any
    @AnyDiscriminator(MapKeyDiscriminatorType.STRING)
    @AnyKeyDefaultDiscriminator("USER")
    @JoinColumn(name = "owner_type")
    @Column(name = "owner_id")
    private Party owner; // used in @NaturalId below

    @NaturalId
    OwnerRef naturalKey; // component containing the @Any
}

// after: key the cache on plain columns only
@Entity
@Cacheable
public class Document {
    @Id Long id;

    @Any(...) // kept as an ordinary attribute, NOT part of the natural id
    private Party owner;

    @NaturalId
    @Column(name = "doc_code")
    private String docCode;
}
Defensive patterns

Strategy: validation

Validate before calling

// Fail fast at startup: cached entities must not have @Any inside id/natural-id components
Metamodel mm = emf.getMetamodel();
for (EntityType<?> et : mm.getEntities()) {
    if (et.getJavaType().isAnnotationPresent(Cacheable.class)) {
        for (Attribute<?, ?> attr : et.getAttributes()) {
            Member m = attr.getJavaMember();
            if (m instanceof Field f && (f.isAnnotationPresent(Any.class)
                    || f.isAnnotationPresent(ManyToAny.class))
                    && (attr.isCollection() || isInNaturalId(et, attr))) {
                throw new IllegalStateException(
                    et.getName() + "." + attr.getName()
                    + " is @Any and cannot participate in a cache key");
            }
        }
    }
}

Prevention

When it happens

Trigger: An @Any or @ManyToAny mapping used as an entity identifier, as part of a composite id, or as a member of a @NaturalId on an entity that is second-level cached (@Cacheable/@Cache); also an @Any embedded in a @Embedded component that participates in a natural id, since ComponentType.disassemble recurses into property types. Calls to Type.disassemble(value, sessionFactory) in custom code on an AnyType hit the same throw.

Common situations: Legacy mappings using <any> inside a composite natural key; adding @Cacheable to an entity that already had a polymorphic @Any reference in its natural id; migrating from Hibernate 5 where a different cache-key path silently serialized the value.

Related errors


AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22). Data as JSON: /api/errors/1ce63f44c1ef74a0. Report an issue: GitHub.