HHH-13129 : Cascaded merge fails for detached bytecode-enhanced entity with uninitialized ToOne

This commit is contained in:
Gail Badner 2019-01-08 19:26:15 -08:00 committed by Guillaume Smet
parent c62f0a75cd
commit a66ca0463e
2 changed files with 37 additions and 17 deletions

View File

@ -12,12 +12,10 @@ import java.util.HashSet;
import java.util.Iterator; import java.util.Iterator;
import org.hibernate.HibernateException; import org.hibernate.HibernateException;
import org.hibernate.bytecode.enhance.spi.LazyPropertyInitializer;
import org.hibernate.bytecode.enhance.spi.interceptor.LazyAttributeLoadingInterceptor; import org.hibernate.bytecode.enhance.spi.interceptor.LazyAttributeLoadingInterceptor;
import org.hibernate.collection.spi.PersistentCollection; import org.hibernate.collection.spi.PersistentCollection;
import org.hibernate.engine.spi.CascadeStyle; import org.hibernate.engine.spi.CascadeStyle;
import org.hibernate.engine.spi.CascadingAction; import org.hibernate.engine.spi.CascadingAction;
import org.hibernate.engine.spi.CascadingActions;
import org.hibernate.engine.spi.CollectionEntry; import org.hibernate.engine.spi.CollectionEntry;
import org.hibernate.engine.spi.EntityEntry; import org.hibernate.engine.spi.EntityEntry;
import org.hibernate.engine.spi.Status; import org.hibernate.engine.spi.Status;
@ -102,25 +100,46 @@ public final class Cascade {
final Object child; final Object child;
if ( isUninitializedProperty ) { if ( isUninitializedProperty ) {
// parent is a bytecode enhanced entity. // parent is a bytecode enhanced entity.
// cascading to an uninitialized, lazy value. // Cascade to an uninitialized, lazy value only if
if ( types[ i ].isCollectionType() ) { // parent is managed in the PersistenceContext.
// The collection does not need to be loaded from the DB. // If parent is a detached entity being merged,
// CollectionType#resolve will return an uninitialized PersistentCollection. // then parent will not be in the PersistencContext
// The action will initialize the collection later, if necessary. // (so lazy attributes must not be initialized).
child = types[ i ].resolve( LazyPropertyInitializer.UNFETCHED_PROPERTY, eventSource, parent ); if ( eventSource.getPersistenceContext().getEntry( parent ) == null ) {
// TODO: it would be nice to be able to set the attribute in parent using // parent was not in the PersistenceContext
// persister.setPropertyValue( parent, i, child ). continue;
// Unfortunately, that would cause the uninitialized collection to be
// loaded from the DB.
} }
else if ( action.performOnLazyProperty() ) { if ( types[ i ].isCollectionType() ) {
// The (non-collection) attribute needs to be initialized so that // CollectionType#getCollection gets the PersistentCollection
// the action can be performed on the initialized attribute. // that corresponds to the uninitialized collection from the
LazyAttributeLoadingInterceptor interceptor = persister.getInstrumentationMetadata().extractInterceptor( parent ); // PersistenceContext. If not present, an uninitialized
// PersistentCollection will be added to the PersistenceContext.
// The action may initialize it later, if necessary.
// This needs to be done even when action.performOnLazyProperty() returns false.
final CollectionType collectionType = (CollectionType) types[i];
child = collectionType.getCollection(
collectionType.getKeyOfOwner( parent, eventSource ),
eventSource,
parent,
null
);
}
else if ( types[ i ].isComponentType() ) {
// Hibernate does not support lazy embeddables, so this shouldn't happen.
throw new UnsupportedOperationException(
"Lazy components are not supported."
);
}
else if ( action.performOnLazyProperty() && types[ i ].isEntityType() ) {
// Only need to initialize a lazy entity attribute when action.performOnLazyProperty()
// returns true.
LazyAttributeLoadingInterceptor interceptor = persister.getInstrumentationMetadata()
.extractInterceptor( parent );
child = interceptor.fetchAttribute( parent, propertyName ); child = interceptor.fetchAttribute( parent, propertyName );
} }
else { else {
// Nothing to do, so just skip cascading to this lazy (non-collection) attribute. // Nothing to do, so just skip cascading to this lazy attribute.
continue; continue;
} }
} }

View File

@ -131,6 +131,7 @@ public class DefaultDeleteEventListener implements DeleteEventListener, Callback
persister, persister,
false false
); );
persister.afterReassociate( entity, source );
} }
else { else {
LOG.trace( "Deleting a persistent instance" ); LOG.trace( "Deleting a persistent instance" );