{"record":{"id":"a8501977253f55e0","repo":"hibernate/hibernate-orm","slug":"sets-don-t-support-updating-by-element","errorCode":null,"errorMessage":"Sets don't support updating by element","messagePattern":"Sets don't support updating by element","errorType":"exception","errorClass":"UnsupportedOperationException","httpStatus":null,"severity":"error","filePath":"hibernate-core/src/main/java/org/hibernate/collection/spi/PersistentSet.java","lineNumber":421,"sourceCode":"\n\t@Override\n\tpublic boolean isRowUpdatePossible() {\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic Object getIndex(Object entry, int i, CollectionPersister persister) {\n\t\tthrow new UnsupportedOperationException(\"Sets don't have indexes\");\n\t}\n\n\t@Override\n\tpublic Object getElement(Object entry) {\n\t\treturn entry;\n\t}\n\n\t@Override\n\tpublic Object getSnapshotElement(Object entry, int i) {\n\t\tthrow new UnsupportedOperationException(\"Sets don't support updating by element\");\n\t}\n\n\t@Override\n\t@SuppressWarnings(\"EqualsWhichDoesntCheckParameterClass\")\n\tpublic boolean equals(Object other) {\n\t\tread();\n\t\treturn set.equals( other );\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\tread();\n\t\treturn set.hashCode();\n\t}\n\n\t@Override\n\tpublic boolean entryExists(Object key, int i) {\n\t\treturn key != null;","sourceCodeStart":403,"sourceCodeEnd":439,"githubUrl":"https://github.com/hibernate/hibernate-orm/blob/fad1729dce015f908198d57a8d80274a30f905a5/hibernate-core/src/main/java/org/hibernate/collection/spi/PersistentSet.java#L403-L439","documentation":"PersistentCollection.getSnapshotElement(entry, i) supplies the element's snapshot state used to decide whether an existing row needs an UPDATE. PersistentSet explicitly reports isRowUpdatePossible() == false — set rows are deleted and re-inserted, never updated — so it cannot provide snapshot elements and throws when asked. Getting here means dirty-check/flush logic (or custom code) expects row updates on a set, the same mapping-mismatch family as getIndex.","triggerScenarios":"A persister or custom code path performing per-element snapshot comparison against a PersistentSet: typically a mapping that implies updatable/indexed collection semantics applied to a Set attribute, or direct internal API calls on the PersistentSet instance.","commonSituations":"Same drift as 'Sets don't have indexes': index or update-oriented metadata left on a field that became a Set; custom collection persisters assuming row updates; copying collections between persistent contexts with mismatched semantics.","solutions":["Make the mapping match set semantics: plain @OneToMany Set / <set>, no index or update-oriented metadata","If elements really are updated in place, map the association as a List/Map or a component collection that supports row updates","Search for custom code calling getSnapshotElement/isRowUpdatePossible on PersistentSet and branch on collection kind","Re-run schema/mapping validation (SessionFactory boot) after every collection type change"],"exampleFix":"// before: Set treated as updatable/indexed collection\n@OneToMany\n@OrderColumn(name = \"pos\")\nprivate Set<Item> items = new HashSet<>();\n\n// after: unordered set, delete-and-reinsert semantics\n@OneToMany\nprivate Set<Item> items = new HashSet<>();","handlingStrategy":"type-guard","validationCode":"if (persistentCollection instanceof org.hibernate.collection.spi.PersistentSet && codePathExpectsRowUpdates()) {\n    throw new IllegalStateException(\"Sets use delete+insert semantics; row updates are not possible\");\n}","typeGuard":"boolean supportsRowUpdates(org.hibernate.collection.spi.PersistentCollection<?> collection) {\n    return !(collection instanceof org.hibernate.collection.spi.PersistentSet); // isRowUpdatePossible() == false\n}","tryCatchPattern":"try {\n    Object snap = collection.getSnapshotElement(entry, i);\n} catch (UnsupportedOperationException e) {\n    // set semantics: compare whole collection, not per-row updates\n    rebuildFromScratch(collection);\n}","preventionTips":["Treat set collections as delete+insert in custom flush/copy logic","Align mapping with collection semantics and re-boot the factory after changes","Prefer List/Map or embeddable-element mappings when in-place row updates are required"],"tags":["hibernate","collections","mapping","persistent-set","snapshot"],"backgroundTag":"collection-mapping-mismatch","analyzedSha":"fad1729dce015f908198d57a8d80274a30f905a5","analyzedAt":"2026-08-22T04:13:57.527Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}