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 org.hibernate.HibernateException;
import org.hibernate.bytecode.enhance.spi.LazyPropertyInitializer;
import org.hibernate.bytecode.enhance.spi.interceptor.LazyAttributeLoadingInterceptor;
import org.hibernate.collection.spi.PersistentCollection;
import org.hibernate.engine.spi.CascadeStyle;
import org.hibernate.engine.spi.CascadingAction;
import org.hibernate.engine.spi.CascadingActions;
import org.hibernate.engine.spi.CollectionEntry;
import org.hibernate.engine.spi.EntityEntry;
import org.hibernate.engine.spi.Status;
@ -102,25 +100,46 @@ public final class Cascade {
final Object child;
if ( isUninitializedProperty ) {
// parent is a bytecode enhanced entity.
// cascading to an uninitialized, lazy value.
if ( types[ i ].isCollectionType() ) {
// The collection does not need to be loaded from the DB.
// CollectionType#resolve will return an uninitialized PersistentCollection.
// The action will initialize the collection later, if necessary.
child = types[ i ].resolve( LazyPropertyInitializer.UNFETCHED_PROPERTY, eventSource, parent );
// TODO: it would be nice to be able to set the attribute in parent using
// persister.setPropertyValue( parent, i, child ).
// Unfortunately, that would cause the uninitialized collection to be
// loaded from the DB.
// Cascade to an uninitialized, lazy value only if
// parent is managed in the PersistenceContext.
// If parent is a detached entity being merged,
// then parent will not be in the PersistencContext
// (so lazy attributes must not be initialized).
if ( eventSource.getPersistenceContext().getEntry( parent ) == null ) {
// parent was not in the PersistenceContext
continue;
}
else if ( action.performOnLazyProperty() ) {
// The (non-collection) attribute needs to be initialized so that
// the action can be performed on the initialized attribute.
LazyAttributeLoadingInterceptor interceptor = persister.getInstrumentationMetadata().extractInterceptor( parent );
if ( types[ i ].isCollectionType() ) {
// CollectionType#getCollection gets the PersistentCollection
// that corresponds to the uninitialized collection from the
// 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 );
}
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;
}
}

View File

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