{"record":{"id":"38ee68664334e5bc","repo":"hibernate/hibernate-orm","slug":"a-collection-with-orphan-deletion-was-no-longer-re","errorCode":null,"errorMessage":"A collection with orphan deletion was no longer referenced by the owning entity instance: {}","messagePattern":"A collection with orphan deletion was no longer referenced by the owning entity instance: (.+?)","errorType":"exception","errorClass":"HibernateException","httpStatus":null,"severity":"error","filePath":"hibernate-core/src/main/java/org/hibernate/engine/internal/Collections.java","lineNumber":70,"sourceCode":"\t\tfinal var loadedPersister = entry.getLoadedPersister();\n\n\t\tif ( loadedPersister != null && CORE_LOGGER.isTraceEnabled() ) {\n\t\t\tCORE_LOGGER.collectionDereferenced(\n\t\t\t\t\tcollectionInfoString( loadedPersister, collection, entry.getLoadedKey(), session ) );\n\t\t}\n\n\t\t// do a check\n\t\tif ( loadedPersister != null && loadedPersister.hasOrphanDelete() ) {\n\t\t\tfinal Object ownerId = getOwnerId( collection, session, loadedPersister );\n\t\t\tfinal var key = session.generateEntityKey( ownerId, loadedPersister.getOwnerEntityPersister() );\n\t\t\tfinal Object owner = persistenceContext.getEntity( key );\n\t\t\t// If owner is null, the owning entity was deleted (removed from persistence context),\n\t\t\t// which is allowed for collections with orphan delete\n\t\t\tif ( owner != null ) {\n\t\t\t\tfinal var entityEntry = persistenceContext.getEntry( owner );\n\t\t\t\t//only collections belonging to deleted entities are allowed to be dereferenced in the case of orphan delete\n\t\t\t\tif ( entityEntry != null && !entityEntry.getStatus().isDeletedOrGone() ) {\n\t\t\t\t\tthrow new HibernateException(\n\t\t\t\t\t\t\t\"A collection with orphan deletion was no longer referenced by the owning entity instance: \"\n\t\t\t\t\t\t\t+ loadedPersister.getRole()\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// do the work\n\t\tentry.setCurrentPersister( null );\n\t\tentry.setCurrentKey( null );\n\t\tprepareCollectionForUpdate( collection, entry, session.getFactory(), flushProcessingContext );\n\n\t}\n\n\tprivate static Object getOwnerId(\n\t\t\tPersistentCollection<?> collection,\n\t\t\tSessionImplementor session,\n\t\t\tCollectionPersister loadedPersister) {","sourceCodeStart":52,"sourceCodeEnd":88,"githubUrl":"https://github.com/hibernate/hibernate-orm/blob/fad1729dce015f908198d57a8d80274a30f905a5/hibernate-core/src/main/java/org/hibernate/engine/internal/Collections.java#L52-L88","documentation":"With orphanRemoval=true, Hibernate must know every element of the loaded collection to decide which children became orphans. At flush, when the processDereferencedCollection path sees the collection no longer referenced by its owner, it checks the owner: if the owner is still managed and not deleted, dereferencing a brand-new collection instance is rejected - Hibernate cannot tell orphans from a wholesale replacement in this configuration, so you must mutate the managed collection rather than reassign the field.","triggerScenarios":"An entity with @OneToMany(..., orphanRemoval = true) (or <one-to-many orphan-delete=\"true\">) where application code replaces the collection property: order.setLines(new ArrayList<>(newLines)), item.setTags(new HashSet<>(tags)), or a mapper (MapStruct etc.) assigning a fresh collection. Thrown at flush from Collections.processDereferencedCollection when the owner entity is still alive in the persistence context.","commonSituations":"DTO-to-entity mappers that build new collection instances; REST update handlers that replace collections wholesale; migrating from orphanRemoval=false where replacement was tolerated; equals/hashCode or cascade refactors that change how collections are assigned.","solutions":["Mutate the existing managed collection in place instead of replacing it: target.getItems().clear(); target.getItems().addAll(newItems);","If a mapper generates the setter call, configure it to update in place (MapStruct uses the getter + clear/addAll via CollectionMappingStrategy).","Drop orphanRemoval=true and delete removed children explicitly via session.remove()/cascade when replacement semantics are required.","If the intent really is to delete everything, delete the owning entity (the check explicitly allows dereference when the owner is deleted/gone)."],"exampleFix":"// before - replaces the collection instance -> HibernateException at flush\norder.setLines(new ArrayList<>(updatedLines));\n// after - reuse the managed collection instance\norder.getLines().clear();\norder.getLines().addAll(updatedLines);","handlingStrategy":"validation","validationCode":"// Guard: for orphanRemoval collections, replace contents in place, never the instance\nstatic <T> void replaceContents(java.util.function.Consumer<Collection<T>> getter,\n                                 Runnable clear, java.util.function.Consumer<T> adder,\n                                 Collection<T> newItems) {\n    clear.run();\n    newItems.forEach(adder);   // mutates the managed PersistentCollection\n}","typeGuard":null,"tryCatchPattern":"catch (HibernateException e) {\n    if (e.getMessage() != null && e.getMessage().startsWith(\"A collection with orphan deletion was no longer referenced\")) {\n        // role name is in the message; switch that setter call to clear()+addAll()\n        throw new MappingMisuseException(e.getMessage(), e);\n    }\n    throw e;\n}","preventionTips":["Never call a collection setter that assigns a new instance on an orphanRemoval association - use clear() then addAll().","Configure MapStruct/BeanUtils copiers to target the existing collection via the getter.","Add an ArchUnit/test rule that bans setXxx(Collection) calls for orphanRemoval-mapped properties."],"tags":["orphanremoval","collection","flush","entity-state","mapping"],"backgroundTag":"orphanremoval-collection-replacement","analyzedSha":"fad1729dce015f908198d57a8d80274a30f905a5","analyzedAt":"2026-08-22T04:13:57.527Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}