HHH-18553 change thrown exception type

and cleanups in DefaultDeleteEventListener

Signed-off-by: Gavin King <gavin@hibernate.org>
This commit is contained in:
Gavin King 2024-09-02 08:33:04 +02:00
parent 7e4df3f805
commit 578023e538
3 changed files with 37 additions and 24 deletions

View File

@ -20,11 +20,23 @@ public class StaleObjectStateException extends StaleStateException {
/** /**
* Constructs a {@code StaleObjectStateException} using the supplied information. * Constructs a {@code StaleObjectStateException} using the supplied information.
*
* @param entityName The name of the entity * @param entityName The name of the entity
* @param identifier The identifier of the entity * @param identifier The identifier of the entity
*/ */
public StaleObjectStateException(String entityName, Object identifier) { public StaleObjectStateException(String entityName, Object identifier) {
super( "Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect)" ); this( entityName, identifier,
"Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect)" );
}
/**
* Constructs a {@code StaleObjectStateException} using the supplied information.
*
* @param entityName The name of the entity
* @param identifier The identifier of the entity
*/
public StaleObjectStateException(String entityName, Object identifier, String message) {
super( message );
this.entityName = entityName; this.entityName = entityName;
this.identifier = identifier; this.identifier = identifier;
} }

View File

@ -9,7 +9,7 @@ package org.hibernate.event.internal;
import org.hibernate.CacheMode; import org.hibernate.CacheMode;
import org.hibernate.HibernateException; import org.hibernate.HibernateException;
import org.hibernate.LockMode; import org.hibernate.LockMode;
import org.hibernate.NonUniqueObjectException; import org.hibernate.StaleObjectStateException;
import org.hibernate.TransientObjectException; import org.hibernate.TransientObjectException;
import org.hibernate.action.internal.CollectionRemoveAction; import org.hibernate.action.internal.CollectionRemoveAction;
import org.hibernate.action.internal.EntityDeleteAction; import org.hibernate.action.internal.EntityDeleteAction;
@ -22,7 +22,6 @@ import org.hibernate.engine.internal.CascadePoint;
import org.hibernate.engine.internal.ForeignKeys; import org.hibernate.engine.internal.ForeignKeys;
import org.hibernate.engine.internal.Nullability; import org.hibernate.engine.internal.Nullability;
import org.hibernate.engine.internal.Nullability.NullabilityCheckType; import org.hibernate.engine.internal.Nullability.NullabilityCheckType;
import org.hibernate.engine.spi.ActionQueue;
import org.hibernate.engine.spi.CascadingActions; import org.hibernate.engine.spi.CascadingActions;
import org.hibernate.engine.spi.EntityEntry; import org.hibernate.engine.spi.EntityEntry;
import org.hibernate.engine.spi.EntityKey; import org.hibernate.engine.spi.EntityKey;
@ -39,7 +38,6 @@ import org.hibernate.internal.FastSessionServices;
import org.hibernate.jpa.event.spi.CallbackRegistry; import org.hibernate.jpa.event.spi.CallbackRegistry;
import org.hibernate.jpa.event.spi.CallbackRegistryConsumer; import org.hibernate.jpa.event.spi.CallbackRegistryConsumer;
import org.hibernate.jpa.event.spi.CallbackType; import org.hibernate.jpa.event.spi.CallbackType;
import org.hibernate.metamodel.spi.MappingMetamodelImplementor;
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;
@ -129,17 +127,16 @@ public class DefaultDeleteEventListener implements DeleteEventListener, Callback
} }
private static void deleteOwnedCollections(Type type, Object key, EventSource session) { private static void deleteOwnedCollections(Type type, Object key, EventSource session) {
final MappingMetamodelImplementor mappingMetamodel = session.getFactory().getMappingMetamodel(); if ( type instanceof CollectionType collectionType ) {
final ActionQueue actionQueue = session.getActionQueue(); final CollectionPersister persister =
if ( type instanceof CollectionType ) { session.getFactory().getMappingMetamodel()
final String role = ( (CollectionType) type ).getRole(); .getCollectionDescriptor( collectionType.getRole() );
final CollectionPersister persister = mappingMetamodel.getCollectionDescriptor(role);
if ( !persister.isInverse() && !skipRemoval( session, persister, key ) ) { if ( !persister.isInverse() && !skipRemoval( session, persister, key ) ) {
actionQueue.addAction( new CollectionRemoveAction( persister, key, session ) ); session.getActionQueue().addAction( new CollectionRemoveAction( persister, key, session ) );
} }
} }
else if ( type instanceof ComponentType ) { else if ( type instanceof ComponentType componentType ) {
final Type[] subtypes = ( (ComponentType) type ).getSubtypes(); final Type[] subtypes = componentType.getSubtypes();
for ( Type subtype : subtypes ) { for ( Type subtype : subtypes ) {
deleteOwnedCollections( subtype, key, session ); deleteOwnedCollections( subtype, key, session );
} }
@ -170,14 +167,14 @@ public class DefaultDeleteEventListener implements DeleteEventListener, Callback
} }
} }
private void deleteDetachedEntity(DeleteEvent event, DeleteContext transientEntities, Object entity, EntityPersister persister, EventSource source) { private void deleteDetachedEntity(
DeleteEvent event, DeleteContext transientEntities, Object entity, EntityPersister persister, EventSource source) {
final Object id = persister.getIdentifier( entity, source ); final Object id = persister.getIdentifier( entity, source );
if ( id == null ) { if ( id == null ) {
throw new TransientObjectException( "Cannot delete instance of entity '" throw new TransientObjectException( "Cannot delete instance of entity '"
+ persister.getEntityName() + "' because it has a null identifier" ); + persister.getEntityName() + "' because it has a null identifier" );
} }
final PersistenceContext persistenceContext = source.getPersistenceContextInternal();
final EntityKey key = source.generateEntityKey( id, persister); final EntityKey key = source.generateEntityKey( id, persister);
final Object version = persister.getVersion(entity); final Object version = persister.getVersion(entity);
@ -186,6 +183,7 @@ public class DefaultDeleteEventListener implements DeleteEventListener, Callback
new OnUpdateVisitor( source, id, entity ).process( entity, persister ); new OnUpdateVisitor( source, id, entity ).process( entity, persister );
final PersistenceContext persistenceContext = source.getPersistenceContextInternal();
final EntityEntry entityEntry = persistenceContext.addEntity( final EntityEntry entityEntry = persistenceContext.addEntity(
entity, entity,
persister.isMutable() ? Status.MANAGED : Status.READ_ONLY, persister.isMutable() ? Status.MANAGED : Status.READ_ONLY,
@ -218,7 +216,8 @@ public class DefaultDeleteEventListener implements DeleteEventListener, Callback
source.evict( existingEntity ); source.evict( existingEntity );
} }
else { else {
throw new NonUniqueObjectException( key.getIdentifier(), key.getEntityName() ); throw new StaleObjectStateException( key.getEntityName(), key.getIdentifier(),
"Persistence context contains a more recent version of the given entity" );
} }
} }
} }
@ -441,7 +440,7 @@ public class DefaultDeleteEventListener implements DeleteEventListener, Callback
final Type[] types = persister.getPropertyTypes(); final Type[] types = persister.getPropertyTypes();
final Object[] deletedState = new Object[types.length]; final Object[] deletedState = new Object[types.length];
if ( !persister.hasCollections() || !persister.hasUninitializedLazyProperties( parent ) ) { if ( !persister.hasCollections() || !persister.hasUninitializedLazyProperties( parent ) ) {
boolean[] copyability = new boolean[types.length]; final boolean[] copyability = new boolean[types.length];
java.util.Arrays.fill( copyability, true ); java.util.Arrays.fill( copyability, true );
TypeHelper.deepCopy( currentState, types, copyability, deletedState, eventSource ); TypeHelper.deepCopy( currentState, types, copyability, deletedState, eventSource );
return deletedState; return deletedState;
@ -450,9 +449,10 @@ public class DefaultDeleteEventListener implements DeleteEventListener, Callback
final String[] propertyNames = persister.getPropertyNames(); final String[] propertyNames = persister.getPropertyNames();
final BytecodeEnhancementMetadata enhancementMetadata = persister.getBytecodeEnhancementMetadata(); final BytecodeEnhancementMetadata enhancementMetadata = persister.getBytecodeEnhancementMetadata();
for ( int i = 0; i < types.length; i++) { for ( int i = 0; i < types.length; i++) {
if ( types[i] instanceof CollectionType && !enhancementMetadata.isAttributeLoaded( parent, propertyNames[i] ) ) { if ( types[i] instanceof CollectionType collectionType
final CollectionType collectionType = (CollectionType) types[i]; && !enhancementMetadata.isAttributeLoaded( parent, propertyNames[i] ) ) {
final CollectionPersister collectionDescriptor = persister.getFactory().getMappingMetamodel() final CollectionPersister collectionDescriptor =
persister.getFactory().getMappingMetamodel()
.getCollectionDescriptor( collectionType.getRole() ); .getCollectionDescriptor( collectionType.getRole() );
if ( collectionDescriptor.needsRemove() || collectionDescriptor.hasCache() ) { if ( collectionDescriptor.needsRemove() || collectionDescriptor.hasCache() ) {
final Object keyOfOwner = collectionType.getKeyOfOwner( parent, eventSource.getSession() ); final Object keyOfOwner = collectionType.getKeyOfOwner( parent, eventSource.getSession() );
@ -491,7 +491,7 @@ public class DefaultDeleteEventListener implements DeleteEventListener, Callback
Object entity, Object entity,
DeleteContext transientEntities) throws HibernateException { DeleteContext transientEntities) throws HibernateException {
CacheMode cacheMode = session.getCacheMode(); final CacheMode cacheMode = session.getCacheMode();
session.setCacheMode( CacheMode.GET ); session.setCacheMode( CacheMode.GET );
final PersistenceContext persistenceContext = session.getPersistenceContextInternal(); final PersistenceContext persistenceContext = session.getPersistenceContextInternal();
persistenceContext.incrementCascadeLevel(); persistenceContext.incrementCascadeLevel();
@ -518,7 +518,7 @@ public class DefaultDeleteEventListener implements DeleteEventListener, Callback
Object entity, Object entity,
DeleteContext transientEntities) throws HibernateException { DeleteContext transientEntities) throws HibernateException {
CacheMode cacheMode = session.getCacheMode(); final CacheMode cacheMode = session.getCacheMode();
session.setCacheMode( CacheMode.GET ); session.setCacheMode( CacheMode.GET );
final PersistenceContext persistenceContext = session.getPersistenceContextInternal(); final PersistenceContext persistenceContext = session.getPersistenceContextInternal();
persistenceContext.incrementCascadeLevel(); persistenceContext.incrementCascadeLevel();

View File

@ -1175,7 +1175,8 @@ public class EntityInitializerImpl extends AbstractInitializer<EntityInitializer
if ( statistics.isStatisticsEnabled() ) { if ( statistics.isStatisticsEnabled() ) {
statistics.optimisticFailure( data.concreteDescriptor.getEntityName() ); statistics.optimisticFailure( data.concreteDescriptor.getEntityName() );
} }
throw new StaleObjectStateException( data.concreteDescriptor.getEntityName(), entry.getId() ); throw new StaleObjectStateException( data.concreteDescriptor.getEntityName(), entry.getId(),
"Query result contains conflicting version of entity already held in persistence context" );
} }
} }