{"record":{"id":"4171d1d33e593cc7","repo":"hibernate/hibernate-orm","slug":"collection-with-orphan-orphan-delete-enabled-has-m","errorCode":null,"errorMessage":"Collection with orphan orphan delete enabled has modifier owner: {}","messagePattern":"Collection with orphan orphan delete enabled has modifier owner: (.+?)","errorType":"exception","errorClass":"HibernateException","httpStatus":null,"severity":"error","filePath":"hibernate-core/src/main/java/org/hibernate/engine/internal/Collections.java","lineNumber":312,"sourceCode":"\t}\n\n\tprivate static void checkOnChangedOwner(PersistentCollection<?> collection, CollectionEntry collectionEntry, CollectionPersister loadedPersister, CollectionPersister currentPersister) {\n\t\tfinal boolean immutableDereferenced =\n\t\t\t\tcollectionEntry.isReadOnly()\n\t\t\t\t&& loadedPersister != null\n\t\t\t\t&& !isOwnerDeletedOrGone( collection );\n\t\tif ( immutableDereferenced ) {\n\t\t\tthrow new HibernateException( \"Immutable collection dereferenced by owner: \"\n\t\t\t\t\t\t+ collectionInfoString( loadedPersister.getRole(), collectionEntry.getLoadedKey() ) );\n\t\t}\n\n\n\t\tfinal boolean orphanDeleteAndRoleChanged =\n\t\t\t\tloadedPersister != null\n\t\t\t\t&& currentPersister != null\n\t\t\t\t&& loadedPersister.hasOrphanDelete();\n\t\tif ( orphanDeleteAndRoleChanged ) {\n\t\t\tthrow new HibernateException(\n\t\t\t\t\t\"Collection with orphan orphan delete enabled has modifier owner: \"\n\t\t\t\t\t+ collectionInfoString( loadedPersister.getRole(), collectionEntry.getLoadedKey() ) );\n\t\t}\n\t}\n\n\tprivate static boolean isOwnerDeletedOrGone(PersistentCollection<?> collection) {\n\t\tfinal Object owner = collection.getOwner();\n\t\tassert owner != null;\n\t\tfinal var session = collection.getSession();\n\t\tassert session != null;\n\t\tfinal var entry = session.getPersistenceContextInternal().getEntry( owner );\n\t\treturn entry != null && entry.getStatus().isDeletedOrGone();\n\t}\n\n\t/**\n\t * Check if the key changed.\n\t * Excludes marking key changed when the loaded key is a {@code DelayedPostInsertIdentifier}.\n\t */","sourceCodeStart":294,"sourceCodeEnd":330,"githubUrl":"https://github.com/hibernate/hibernate-orm/blob/fad1729dce015f908198d57a8d80274a30f905a5/hibernate-core/src/main/java/org/hibernate/engine/internal/Collections.java#L294-L330","documentation":"The second guard in checkOnChangedOwner: when a collection was loaded under one persister (role) but at flush its current persister is a DIFFERENT non-null role and the loaded side has orphanRemoval, Hibernate throws. In plain terms, a managed collection instance with orphan-delete semantics ended up owned by a different association/owner - Hibernate cannot decide orphan deletion across roles, so the owner change is rejected (note the message text is garbled in the source: 'orphan orphan delete ... modifier owner').","triggerScenarios":"Reassigning a Hibernate-managed collection instance from one entity's orphanRemoval=true association to another entity/property with a different role: b.setScores(a.getScores()) where Scores has orphanRemoval; re-parenting logic that moves collection references between associations; merge of graphs where collections migrate between owners.","commonSituations":"Refactoring that moves a collection property from one entity to another while old data paths still copy the reference; batch code 'recycling' collections between rows; copy/paste wiring of bidirectional relations on orphanRemoval associations.","solutions":["Never move a managed collection instance between associations - create a new collection and copy elements.","Re-model re-parenting as explicit element moves: remove child from A's collection, add to B's collection (each association keeps its own instance).","Remove orphanRemoval=true if wholesale reassignment between owners is a legitimate operation in your domain (then delete orphans manually).","Find the offending assignment via the role name in the message (collectionInfoString includes role and key)."],"exampleFix":"// before - managed collection with orphanRemoval moved to another owner\nstudentB.setScores(studentA.getScores());\n// after - new instance per owner, elements moved explicitly\nstudentB.setScores(new ArrayList<>(studentA.getScores()));\nstudentA.setScores(new ArrayList<>());","handlingStrategy":"validation","validationCode":"// Guard: block owner changes for managed orphanRemoval collections\nstatic <T> void moveToOtherOwner(List<T> from, List<T> to, T item) {\n    from.remove(item);\n    if (to == from) throw new IllegalArgumentException(\"same collection instance reused across owners\");\n    to.add(item);   // each association keeps its own collection instance\n}","typeGuard":"static boolean isPersistentCollection(Object c) {\n    return c instanceof org.hibernate.collection.spi.PersistentCollection;\n}","tryCatchPattern":"catch (HibernateException e) {\n    if (e.getMessage() != null && e.getMessage().contains(\"orphan delete enabled has modifier owner\")) {\n        // role+key in message; replace the cross-owner assignment with element moves\n        throw new MappingMisuseException(e.getMessage(), e);\n    }\n    throw e;\n}","preventionTips":["Model re-parenting with element add/remove on each side's own collection.","Never assign a getter-returned collection to a different association property.","Use the role name in the message to grep for the offending setter call."],"tags":["orphanremoval","collection","owner-change","flush","mapping"],"backgroundTag":"orphanremoval-collection-replacement","analyzedSha":"fad1729dce015f908198d57a8d80274a30f905a5","analyzedAt":"2026-08-22T04:13:57.527Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}