HHH-9663 - Fix orphan removal cascade for one-to-one mappings.

This commit is contained in:
Chris Cranford 2017-04-07 12:34:05 -04:00 committed by Gail Badner
parent 85732409ee
commit 63e6793d8f
1 changed files with 73 additions and 20 deletions

View File

@ -26,6 +26,7 @@ import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.persister.collection.CollectionPersister; import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.persister.entity.EntityPersister; import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.pretty.MessageHelper; import org.hibernate.pretty.MessageHelper;
import org.hibernate.proxy.HibernateProxy;
import org.hibernate.type.AssociationType; import org.hibernate.type.AssociationType;
import org.hibernate.type.CollectionType; import org.hibernate.type.CollectionType;
import org.hibernate.type.CompositeType; import org.hibernate.type.CompositeType;
@ -90,9 +91,8 @@ public final class Cascade {
final CascadeStyle style = cascadeStyles[ i ]; final CascadeStyle style = cascadeStyles[ i ];
final String propertyName = propertyNames[ i ]; final String propertyName = propertyNames[ i ];
if ( style.doCascade( action ) ) { Object child = null;
Object child; if ( style.doCascade( action ) || action.deleteOrphans() ) {
if ( hasUninitializedLazyProperties && if ( hasUninitializedLazyProperties &&
!persister.getInstrumentationMetadata().isAttributeLoaded( parent, propertyName ) ) { !persister.getInstrumentationMetadata().isAttributeLoaded( parent, propertyName ) ) {
// parent is a bytecode enhanced entity. // parent is a bytecode enhanced entity.
@ -115,13 +115,24 @@ public final class Cascade {
} }
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 (non-collection) attribute.
if ( action.requiresNoCascadeChecking() ) {
action.noCascade(
eventSource,
parent,
persister,
types[ i ],
i
);
}
continue; continue;
} }
} }
else { else {
child = persister.getPropertyValue( parent, i ); child = persister.getPropertyValue( parent, i );
} }
}
if ( style.doCascade( action ) ) {
cascadeProperty( cascadeProperty(
action, action,
cascadePoint, cascadePoint,
@ -144,6 +155,19 @@ public final class Cascade {
types[ i ], types[ i ],
i i
); );
if ( action.deleteOrphans() ) {
cascadeLogicalOneToOneOrphanRemoval(
action,
eventSource,
componentPathStackDepth,
parent,
child,
types[ i ],
style,
propertyName,
false
);
}
} }
} }
@ -201,6 +225,29 @@ public final class Cascade {
} }
} }
cascadeLogicalOneToOneOrphanRemoval(
action,
eventSource,
componentPathStackDepth,
parent,
child,
type,
style,
propertyName,
isCascadeDeleteEnabled );
}
private static void cascadeLogicalOneToOneOrphanRemoval(
final CascadingAction action,
final EventSource eventSource,
final int componentPathStackDepth,
final Object parent,
final Object child,
final Type type,
final CascadeStyle style,
final String propertyName,
final boolean isCascadeDeleteEnabled) throws HibernateException {
// potentially we need to handle orphan deletes for one-to-ones here... // potentially we need to handle orphan deletes for one-to-ones here...
if ( isLogicalOneToOne( type ) ) { if ( isLogicalOneToOne( type ) ) {
// We have a physical or logical one-to-one. See if the attribute cascade settings and action-type require // We have a physical or logical one-to-one. See if the attribute cascade settings and action-type require
@ -210,7 +257,7 @@ public final class Cascade {
// because it is currently null. // because it is currently null.
final EntityEntry entry = eventSource.getPersistenceContext().getEntry( parent ); final EntityEntry entry = eventSource.getPersistenceContext().getEntry( parent );
if ( entry != null && entry.getStatus() != Status.SAVING ) { if ( entry != null && entry.getStatus() != Status.SAVING ) {
final Object loadedValue; Object loadedValue;
if ( componentPathStackDepth == 0 ) { if ( componentPathStackDepth == 0 ) {
// association defined on entity // association defined on entity
loadedValue = entry.getLoadedValue( propertyName ); loadedValue = entry.getLoadedValue( propertyName );
@ -236,11 +283,17 @@ public final class Cascade {
// orphaned if the association was nulled (child == null) or receives a new value while the // orphaned if the association was nulled (child == null) or receives a new value while the
// entity is managed (without first nulling and manually flushing). // entity is managed (without first nulling and manually flushing).
if ( child == null || ( loadedValue != null && child != loadedValue ) ) { if ( child == null || ( loadedValue != null && child != loadedValue ) ) {
final EntityEntry valueEntry = eventSource EntityEntry valueEntry = eventSource
.getPersistenceContext().getEntry( .getPersistenceContext().getEntry(
loadedValue ); loadedValue );
// Need to check this in case the context has
// already been flushed. See HHH-7829. if ( valueEntry == null && loadedValue instanceof HibernateProxy ) {
// un-proxy and re-associate for cascade operation
// useful for @OneToOne defined as FetchType.LAZY
loadedValue = eventSource.getPersistenceContext().unproxyAndReassociate( loadedValue );
valueEntry = eventSource.getPersistenceContext().getEntry( loadedValue );
}
if ( valueEntry != null ) { if ( valueEntry != null ) {
final String entityName = valueEntry.getPersister().getEntityName(); final String entityName = valueEntry.getPersister().getEntityName();
if ( LOG.isTraceEnabled() ) { if ( LOG.isTraceEnabled() ) {