The fix for HHH-6848 Closed causes IllegalStateException when merging an entity results in merging more than one representation of the same detached entity.
For example, Hibernate throws IllegalStateException when merging entity 'x' if it has references to 2 detached entities 'y1' and 'y2' (obtained from different sessions), and y1 and y2 represent the same persistent entity.
Documentation will be added to the manual by HHH-9216 Closed . Here is some documentation to get people going.
A new configuration property, hibernate.event.merge.entity_copy_observer, controls how Hibernate will respond when multiple representations of the same persistent entity ("entity copy") is detected while merging.
hibernate.event.merge.entity_copy_observer can be set to one of the following values:
- disallow (the default): throws IllegalStateException if an entity copy is detected;
- allow: performs the merge operation on each entity copy that is detected;
- log: (provided for testing only) performs the merge operation on each entity copy that is detected and logs information about the entity copies. This setting requires DEBUG logging be enabled for org.hibernate.event.internal.EntityCopyAllowedLoggedObserver.
In addition the application may customize the behavior by providing an implementation of org.hibernate.event.spi.EntityCopyObserver and setting hibernate.event.merge.entity_copy_observer to the class name.
When hibernate.event.merge.entity_copy_observer=allow or log, Hibernate will merge each entity copy detected while cascading the merge operation. In the process of merging each entity copy, Hibernate will cascade the merge operation from each entity copy to its assocations with cascade=CascadeType.MERGE or CascadeType.ALL. The entity state resulting from merging an entity copy will be overwritten when another entity copy is merged.
There are no known issues with merging multiple entity copies as long as they are consistent (i.e. have the same property/association values).
RISKS OF MERGING ENTITY COPIES:
Because cascade order is undefined, the order in which the entity copies are merged is undefined. As a result, if property values in the entity copies are not consistent, the resulting entity state will be indeterminate and data will be lost from all entity copies except for the last one merged.
If an entity copy cascades the merge operation to an association that is (or contains) a new entity, that new entity will be merged (i.e, persisted and the merge operation will be cascaded to its associations according to its mapping), even if that same association is ultimately overwritten when Hibernate merges a different representation having a different value for its association. If the association is mapped with orphanRemoval=true, the new entity will not be deleted because the semantics of orphanRemoval do not apply if the entity being orphaned is a new entity.
There are known issues when representations of the same persistent entity have different values for a collection. See HHH-9239 Open and HHH-9240 Open for details. These issues can cause data loss or corruption.
By setting hibernate.event.merge.entity_copy_observer=allow or log, Hibernate will allow entity copies of any type of entity to be merged. The only way to exclude particular entity classes or associations that contain critical data is to provide a custom implementation of org.hibernate.event.spi.EntityCopyObserver with the desired behavior, and setting hibernate.event.merge.entity_copy_observer to the class name.
Hibernate provides limited DEBUG logging capabilities that can help determine the entity classes for which entity copies were found. By setting hibernate.event.merge.entity_copy_observer=log and enabling DEBUG logging for org.hibernate.event.internal.EntityCopyAllowedLoggedObserver, the following will be logged each time an application calls EntityManager.merge( entity ) or Session.merge( entity ):
- number of times multiple representations of the same persistent entity was detected summarized by entity name;
- details by entity name and ID, including output from calling toString() on each representation being merged as well as the merge result.
The log should be reviewed to determine if multiple representations of entities containing critical data are detected. If so, the application should be modified so there is only one representation, and a custom implementation of org.hibernate.event.spi.EntityCopyObserver should be provided to disallow entity copies for entities with critical data.
Using optimistic locking is recommended to detect if different representations are from different versions of the same persistent entity. If they are not from the same version, Hibernate will throw StaleObjectStateException.