HHH-16907 moved if from inside private method to around

This improves performance of cascadeLogicalOneToOneOrphanRemoval
handling, because entity fields are only read by reflection if the field
is actually a 1:1 relation (before the change, this was done every time
for every field)
This commit is contained in:
norbert_wirges 2023-07-11 14:06:18 +02:00 committed by Steve Ebersole
parent 7c378847cb
commit f55c017f59
1 changed files with 85 additions and 86 deletions

View File

@ -174,7 +174,7 @@ public final class Cascade {
); );
} }
// If the property is uninitialized, then there cannot be any orphans. // If the property is uninitialized, then there cannot be any orphans.
if ( action.deleteOrphans() && !isUninitializedProperty ) { if ( action.deleteOrphans() && !isUninitializedProperty && isLogicalOneToOne( type ) ) {
cascadeLogicalOneToOneOrphanRemoval( cascadeLogicalOneToOneOrphanRemoval(
action, action,
eventSource, eventSource,
@ -252,19 +252,21 @@ public final class Cascade {
} }
} }
} }
if ( isLogicalOneToOne( type ) ) {
cascadeLogicalOneToOneOrphanRemoval( cascadeLogicalOneToOneOrphanRemoval(
action, action,
eventSource, eventSource,
componentPath, componentPath,
parent, parent,
child, child,
type, type,
style, style,
propertyName, propertyName,
isCascadeDeleteEnabled ); isCascadeDeleteEnabled );
}
} }
/** potentially we need to handle orphan deletes for one-to-ones here...*/
private static <T> void cascadeLogicalOneToOneOrphanRemoval( private static <T> void cascadeLogicalOneToOneOrphanRemoval(
final CascadingAction<T> action, final CascadingAction<T> action,
final EventSource eventSource, final EventSource eventSource,
@ -276,88 +278,85 @@ public final class Cascade {
final String propertyName, final String propertyName,
final boolean isCascadeDeleteEnabled) throws HibernateException { final boolean isCascadeDeleteEnabled) throws HibernateException {
// potentially we need to handle orphan deletes for one-to-ones here... // We have a physical or logical one-to-one. See if the attribute cascade settings and action-type require
if ( isLogicalOneToOne( type ) ) { // orphan checking
// We have a physical or logical one-to-one. See if the attribute cascade settings and action-type require if ( style.hasOrphanDelete() && action.deleteOrphans() ) {
// orphan checking // value is orphaned if loaded state for this property shows not null
if ( style.hasOrphanDelete() && action.deleteOrphans() ) { // because it is currently null.
// value is orphaned if loaded state for this property shows not null final PersistenceContext persistenceContext = eventSource.getPersistenceContextInternal();
// because it is currently null. final EntityEntry entry = persistenceContext.getEntry( parent );
final PersistenceContext persistenceContext = eventSource.getPersistenceContextInternal(); if ( entry != null && entry.getStatus() != Status.SAVING ) {
final EntityEntry entry = persistenceContext.getEntry( parent ); Object loadedValue;
if ( entry != null && entry.getStatus() != Status.SAVING ) { if ( componentPath == null ) {
Object loadedValue; // association defined on entity
if ( componentPath == null ) { loadedValue = entry.getLoadedValue( propertyName );
// association defined on entity }
loadedValue = entry.getLoadedValue( propertyName ); else {
// association defined on component
// Since the loadedState in the EntityEntry is a flat domain type array
// We first have to extract the component object and then ask the component type
// recursively to give us the value of the sub-property of that object
final Type propertyType = entry.getPersister().getPropertyType( componentPath.get(0) );
if ( propertyType instanceof ComponentType ) {
loadedValue = entry.getLoadedValue( componentPath.get( 0 ) );
ComponentType componentType = (ComponentType) propertyType;
if ( componentPath.size() != 1 ) {
for ( int i = 1; i < componentPath.size(); i++ ) {
final int subPropertyIndex = componentType.getPropertyIndex( componentPath.get( i ) );
loadedValue = componentType.getPropertyValue( loadedValue, subPropertyIndex );
componentType = (ComponentType) componentType.getSubtypes()[subPropertyIndex];
}
}
loadedValue = componentType.getPropertyValue( loadedValue, componentType.getPropertyIndex( propertyName ) );
} }
else { else {
// association defined on component // Association is probably defined in an element collection, so we can't do orphan removals
// Since the loadedState in the EntityEntry is a flat domain type array loadedValue = null;
// We first have to extract the component object and then ask the component type }
// recursively to give us the value of the sub-property of that object }
final Type propertyType = entry.getPersister().getPropertyType( componentPath.get(0) );
if ( propertyType instanceof ComponentType ) {
loadedValue = entry.getLoadedValue( componentPath.get( 0 ) );
ComponentType componentType = (ComponentType) propertyType;
if ( componentPath.size() != 1 ) {
for ( int i = 1; i < componentPath.size(); i++ ) {
final int subPropertyIndex = componentType.getPropertyIndex( componentPath.get( i ) );
loadedValue = componentType.getPropertyValue( loadedValue, subPropertyIndex );
componentType = (ComponentType) componentType.getSubtypes()[subPropertyIndex];
}
}
loadedValue = componentType.getPropertyValue( loadedValue, componentType.getPropertyIndex( propertyName ) ); // orphaned if the association was nulled (child == null) or receives a new value while the
} // entity is managed (without first nulling and manually flushing).
else { if ( child == null || loadedValue != null && child != loadedValue ) {
// Association is probably defined in an element collection, so we can't do orphan removals EntityEntry valueEntry = persistenceContext.getEntry( loadedValue );
loadedValue = null;
if ( valueEntry == null && isHibernateProxy( loadedValue ) ) {
// un-proxy and re-associate for cascade operation
// useful for @OneToOne defined as FetchType.LAZY
loadedValue = persistenceContext.unproxyAndReassociate( loadedValue );
valueEntry = persistenceContext.getEntry( loadedValue );
// HHH-11965
// Should the unwrapped proxy value be equal via reference to the entity's property value
// provided by the 'child' variable, we should not trigger the orphan removal of the
// associated one-to-one.
if ( child == loadedValue ) {
// do nothing
return;
} }
} }
// orphaned if the association was nulled (child == null) or receives a new value while the if ( valueEntry != null ) {
// entity is managed (without first nulling and manually flushing). final EntityPersister persister = valueEntry.getPersister();
if ( child == null || loadedValue != null && child != loadedValue ) { final String entityName = persister.getEntityName();
EntityEntry valueEntry = persistenceContext.getEntry( loadedValue ); if ( LOG.isTraceEnabled() ) {
LOG.tracev(
if ( valueEntry == null && isHibernateProxy( loadedValue ) ) { "Deleting orphaned entity instance: {0}",
// un-proxy and re-associate for cascade operation infoString( entityName, persister.getIdentifier( loadedValue, eventSource ) )
// useful for @OneToOne defined as FetchType.LAZY );
loadedValue = persistenceContext.unproxyAndReassociate( loadedValue );
valueEntry = persistenceContext.getEntry( loadedValue );
// HHH-11965
// Should the unwrapped proxy value be equal via reference to the entity's property value
// provided by the 'child' variable, we should not trigger the orphan removal of the
// associated one-to-one.
if ( child == loadedValue ) {
// do nothing
return;
}
} }
if ( valueEntry != null ) { if ( type.isAssociationType()
final EntityPersister persister = valueEntry.getPersister(); && ( (AssociationType) type ).getForeignKeyDirection().equals(TO_PARENT) ) {
final String entityName = persister.getEntityName(); // If FK direction is to-parent, we must remove the orphan *before* the queued update(s)
if ( LOG.isTraceEnabled() ) { // occur. Otherwise, replacing the association on a managed entity, without manually
LOG.tracev( // nulling and flushing, causes FK constraint violations.
"Deleting orphaned entity instance: {0}", eventSource.removeOrphanBeforeUpdates( entityName, loadedValue );
infoString( entityName, persister.getIdentifier( loadedValue, eventSource ) ) }
); else {
} // Else, we must delete after the updates.
eventSource.delete( entityName, loadedValue, isCascadeDeleteEnabled, DeleteContext.create() );
if ( type.isAssociationType()
&& ( (AssociationType) type ).getForeignKeyDirection().equals(TO_PARENT) ) {
// If FK direction is to-parent, we must remove the orphan *before* the queued update(s)
// occur. Otherwise, replacing the association on a managed entity, without manually
// nulling and flushing, causes FK constraint violations.
eventSource.removeOrphanBeforeUpdates( entityName, loadedValue );
}
else {
// Else, we must delete after the updates.
eventSource.delete( entityName, loadedValue, isCascadeDeleteEnabled, DeleteContext.create() );
}
} }
} }
} }