hibernate/hibernate-orm · error · HibernateException
Collection with orphan orphan delete enabled has modifier ow
Error message
Collection with orphan orphan delete enabled has modifier owner: {} What it means
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').
Source
Thrown at hibernate-core/src/main/java/org/hibernate/engine/internal/Collections.java:312
}
private static void checkOnChangedOwner(PersistentCollection<?> collection, CollectionEntry collectionEntry, CollectionPersister loadedPersister, CollectionPersister currentPersister) {
final boolean immutableDereferenced =
collectionEntry.isReadOnly()
&& loadedPersister != null
&& !isOwnerDeletedOrGone( collection );
if ( immutableDereferenced ) {
throw new HibernateException( "Immutable collection dereferenced by owner: "
+ collectionInfoString( loadedPersister.getRole(), collectionEntry.getLoadedKey() ) );
}
final boolean orphanDeleteAndRoleChanged =
loadedPersister != null
&& currentPersister != null
&& loadedPersister.hasOrphanDelete();
if ( orphanDeleteAndRoleChanged ) {
throw new HibernateException(
"Collection with orphan orphan delete enabled has modifier owner: "
+ collectionInfoString( loadedPersister.getRole(), collectionEntry.getLoadedKey() ) );
}
}
private static boolean isOwnerDeletedOrGone(PersistentCollection<?> collection) {
final Object owner = collection.getOwner();
assert owner != null;
final var session = collection.getSession();
assert session != null;
final var entry = session.getPersistenceContextInternal().getEntry( owner );
return entry != null && entry.getStatus().isDeletedOrGone();
}
/**
* Check if the key changed.
* Excludes marking key changed when the loaded key is a {@code DelayedPostInsertIdentifier}.
*/View on GitHub (pinned to fad1729dce)
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).
Example fix
// before - managed collection with orphanRemoval moved to another owner studentB.setScores(studentA.getScores()); // after - new instance per owner, elements moved explicitly studentB.setScores(new ArrayList<>(studentA.getScores())); studentA.setScores(new ArrayList<>());
Defensive patterns
Strategy: validation
Validate before calling
// Guard: block owner changes for managed orphanRemoval collections
static <T> void moveToOtherOwner(List<T> from, List<T> to, T item) {
from.remove(item);
if (to == from) throw new IllegalArgumentException("same collection instance reused across owners");
to.add(item); // each association keeps its own collection instance
} Type guard
static boolean isPersistentCollection(Object c) {
return c instanceof org.hibernate.collection.spi.PersistentCollection;
} Try / catch
catch (HibernateException e) {
if (e.getMessage() != null && e.getMessage().contains("orphan delete enabled has modifier owner")) {
// role+key in message; replace the cross-owner assignment with element moves
throw new MappingMisuseException(e.getMessage(), e);
}
throw e;
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- A collection with orphan deletion was no longer referenced b
- Found two representations of same collection: {}
- Found shared references to a collection: {}
- cannot recreate collection while filter is enabled: " + coll
- cannot recreate collection while filter is enabled [%s : %s]
AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22).
Data as JSON: /api/errors/4171d1d33e593cc7.
Report an issue: GitHub.