hibernate/hibernate-orm · error · UnsupportedOperationException
CollectionType not supported as part of cache key!
Error message
CollectionType not supported as part of cache key!
What it means
CollectionType overrides Type.disassemble(Object, SessionFactoryImplementor) (CollectionType.java:261) to throw UnsupportedOperationException ('CollectionType not supported as part of cache key!'), because a collection is a role/fk construct, not a serializable value. The two-argument disassemble is used by DefaultCacheKeysFactory when building entity-id and natural-id cache keys, so the error means a collection-typed value ended up in a position that must be cache-key material (identifier, natural id, or a component used in one).
Source
Thrown at hibernate-core/src/main/java/org/hibernate/type/CollectionType.java:262
@Override
public Serializable disassemble(Object value, SharedSessionContractImplementor session, Object owner)
throws HibernateException {
//remember the uk value
//This solution would allow us to eliminate the owner arg to disassemble(), but
//what if the collection was null, and then later had elements added? seems unsafe
//session.getPersistenceContext().getCollectionEntry( (PersistentCollection) value ).getKey();
final Object key = getKeyOfOwner( owner, session );
return key == null ? null
: getPersister( session )
.getKeyType()
.disassemble( key, session, owner );
}
@Override
public Serializable disassemble(Object value, SessionFactoryImplementor sessionFactory) throws HibernateException {
throw new UnsupportedOperationException( "CollectionType not supported as part of cache key!" );
}
@Override
public Object assemble(Serializable cached, SharedSessionContractImplementor session, Object owner)
throws HibernateException {
//we must use the "remembered" uk value, since it is
//not available from the EntityEntry during assembly
if ( cached == null ) {
return null;
}
else {
final Object key =
getPersister( session )
.getKeyType()
.assemble( cached, session, owner);
return resolveKey( key, session, owner );
}
}View on GitHub (pinned to fad1729dce)
Solutions
- Remove collection-valued members from identifiers and natural ids; key entities on scalar/basic values only.
- If the collection was meant to be data, move it to a normal association (one-to-many) keyed by the owner's id, not into the key.
- Audit generic caching code that calls disassemble(value, sessionFactory) and skip CollectionType/AnyType instances.
- Validate mappings at startup (SessionFactory schema validation pass or a custom MappingMetadata check) to catch key-position collections before runtime.
Example fix
// before
@Embeddable
public class NaturalKey implements Serializable {
String code;
@OneToMany(mappedBy = "doc")
Set<Tag> tags; // collection inside a @NaturalId component
}
// after: keep the natural id scalar-only
@Embeddable
public class NaturalKey implements Serializable {
String code;
}
@Entity
public class Doc {
@Embedded @NaturalId NaturalKey key;
@OneToMany(mappedBy = "doc")
Set<Tag> tags; // ordinary association, not part of the key
} Defensive patterns
Strategy: validation
Validate before calling
// Startup check: no collection-typed members inside id/natural-id components
for (EntityType<?> et : emf.getMetamodel().getEntities()) {
et.getSingularAttributes().stream()
.filter(a -> a.isId() || isNaturalId(et, a))
.filter(a -> ((SingularAttribute<?, ?>) a).getType().getPersistenceType()
== Type.PersistenceType.EMBEDDABLE)
.forEach(a -> {
for (Attribute<?, ?> sub : ((EmbeddableType<?>) ((SingularAttribute<?, ?>) a).getType()).getAttributes()) {
if (sub.isCollection()) {
throw new IllegalStateException(
et.getName() + "." + a.getName() + "." + sub.getName()
+ ": collections cannot be part of a cache key");
}
}
});
} Prevention
- Keep identifiers and natural ids composed of basic types and simple embeddables only.
- Review any generic layer that disassembles property types for caching and exclude CollectionType.
When it happens
Trigger: Mapping a collection type where a scalar is required: an @Id/@EmbeddedId component containing a collection-typed member, a @NaturalId containing a collection, or custom code calling Type.disassemble(value, sessionFactory) on a collection role's type; also mismatched generic frameworks that treat any property type as cache-key-able.
Common situations: Hand-written hbm.xml with <composite-id> accidentally including a <set>; JPA entities where a @NaturalId-annotated embeddable gains a collection field during refactoring; generic audit/caching layers that disassemble every property type into cache keys.
Related errors
- Caching was not configured for entity natural id:
- AnyType not supported as part of cache key!
- Duplicate collection definition '%s'
- Unexpected node type -
- @EmbeddedTable only supported for use on entity or mapped-su
AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22).
Data as JSON: /api/errors/9a5cc3e9f672dabf.
Report an issue: GitHub.