{"record":{"id":"9a5cc3e9f672dabf","repo":"hibernate/hibernate-orm","slug":"collectiontype-not-supported-as-part-of-cache-key","errorCode":null,"errorMessage":"CollectionType not supported as part of cache key!","messagePattern":"CollectionType not supported as part of cache key!","errorType":"exception","errorClass":"UnsupportedOperationException","httpStatus":null,"severity":"error","filePath":"hibernate-core/src/main/java/org/hibernate/type/CollectionType.java","lineNumber":262,"sourceCode":"\t@Override\n\tpublic Serializable disassemble(Object value, SharedSessionContractImplementor session, Object owner)\n\t\t\tthrows HibernateException {\n\t\t//remember the uk value\n\n\t\t//This solution would allow us to eliminate the owner arg to disassemble(), but\n\t\t//what if the collection was null, and then later had elements added? seems unsafe\n\t\t//session.getPersistenceContext().getCollectionEntry( (PersistentCollection) value ).getKey();\n\n\t\tfinal Object key = getKeyOfOwner( owner, session );\n\t\treturn key == null ? null\n\t\t\t\t: getPersister( session )\n\t\t\t\t\t\t.getKeyType()\n\t\t\t\t\t\t.disassemble( key, session, owner );\n\t}\n\n\t@Override\n\tpublic Serializable disassemble(Object value, SessionFactoryImplementor sessionFactory) throws HibernateException {\n\t\tthrow new UnsupportedOperationException( \"CollectionType not supported as part of cache key!\" );\n\t}\n\n\t@Override\n\tpublic Object assemble(Serializable cached, SharedSessionContractImplementor session, Object owner)\n\t\t\tthrows HibernateException {\n\t\t//we must use the \"remembered\" uk value, since it is\n\t\t//not available from the EntityEntry during assembly\n\t\tif ( cached == null ) {\n\t\t\treturn null;\n\t\t}\n\t\telse {\n\t\t\tfinal Object key =\n\t\t\t\t\tgetPersister( session )\n\t\t\t\t\t\t\t.getKeyType()\n\t\t\t\t\t\t\t.assemble( cached, session, owner);\n\t\t\treturn resolveKey( key, session, owner );\n\t\t}\n\t}","sourceCodeStart":244,"sourceCodeEnd":280,"githubUrl":"https://github.com/hibernate/hibernate-orm/blob/fad1729dce015f908198d57a8d80274a30f905a5/hibernate-core/src/main/java/org/hibernate/type/CollectionType.java#L244-L280","documentation":"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).","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\n@Embeddable\npublic class NaturalKey implements Serializable {\n    String code;\n    @OneToMany(mappedBy = \"doc\")\n    Set<Tag> tags; // collection inside a @NaturalId component\n}\n\n// after: keep the natural id scalar-only\n@Embeddable\npublic class NaturalKey implements Serializable {\n    String code;\n}\n\n@Entity\npublic class Doc {\n    @Embedded @NaturalId NaturalKey key;\n    @OneToMany(mappedBy = \"doc\")\n    Set<Tag> tags; // ordinary association, not part of the key\n}","handlingStrategy":"validation","validationCode":"// Startup check: no collection-typed members inside id/natural-id components\nfor (EntityType<?> et : emf.getMetamodel().getEntities()) {\n    et.getSingularAttributes().stream()\n      .filter(a -> a.isId() || isNaturalId(et, a))\n      .filter(a -> ((SingularAttribute<?, ?>) a).getType().getPersistenceType()\n                   == Type.PersistenceType.EMBEDDABLE)\n      .forEach(a -> {\n          for (Attribute<?, ?> sub : ((EmbeddableType<?>) ((SingularAttribute<?, ?>) a).getType()).getAttributes()) {\n              if (sub.isCollection()) {\n                  throw new IllegalStateException(\n                      et.getName() + \".\" + a.getName() + \".\" + sub.getName()\n                      + \": collections cannot be part of a cache key\");\n              }\n          }\n      });\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["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."],"tags":["hibernate","collection-mapping","second-level-cache","natural-id","composite-id"],"backgroundTag":"cache-key-unsupported-type","analyzedSha":"fad1729dce015f908198d57a8d80274a30f905a5","analyzedAt":"2026-08-22T04:13:57.527Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}