HHH-17122 clean up old code in save event listeners, and finish id from @PrePersist

This commit is contained in:
Gavin King 2023-08-23 11:00:14 +02:00
parent bead7dec50
commit 0b4cc3cc7e
4 changed files with 100 additions and 134 deletions

View File

@ -78,17 +78,8 @@ public abstract class AbstractSaveEventListener<C>
String entityName,
C context,
EventSource source) {
callbackRegistry.preCreate( entity );
return performSave(
entity,
requestedId,
source.getEntityPersister( entityName, entity ),
false,
context,
source,
true
);
final EntityPersister persister = source.getEntityPersister( entityName, entity );
return performSave( entity, requestedId, persister, false, context, source, false );
}
/**
@ -99,9 +90,9 @@ public abstract class AbstractSaveEventListener<C>
* @param context Generally cascade-specific information.
* @param source The session which is the source of this save event.
* @param requiresImmediateIdAccess does the event context require
* access to the identifier immediately after execution of this method (if
* not, post-insert style id generators may be postponed if we are outside
* a transaction).
* access to the identifier immediately after execution of this method
* (if not, post-insert style id generators may be postponed if we are
* outside a transaction).
*
* @return The id used to save the entity; may be null depending on the
* type of id generator used and the requiresImmediateIdAccess value
@ -114,35 +105,65 @@ public abstract class AbstractSaveEventListener<C>
boolean requiresImmediateIdAccess) {
final EntityPersister persister = source.getEntityPersister( entityName, entity );
final Generator generator = persister.getGenerator();
if ( !generator.generatedOnExecution() ) {
final BeforeExecutionGenerator beforeExecutionGenerator = (BeforeExecutionGenerator) generator;
final Object generatedId;
if ( generator instanceof Assigned ) {
// get it from the entity later, since we need
// the preCreate() callback to happen first
generatedId = null;
final boolean generatedOnExecution = generator.generatedOnExecution();
final Object generatedId;
if ( generatedOnExecution ) {
// the id gets generated by the database
// and is not yet available
generatedId = null;
}
else if ( generator instanceof Assigned ) {
// get it from the entity later, since we need
// the @PrePersist callback to happen first
generatedId = null;
}
else {
// go ahead and generate id, and then set it to
// the entity instance, so it will be available
// to the entity in the @PrePersist callback
generatedId = generateId( entity, source, (BeforeExecutionGenerator) generator, persister );
if ( generatedId == SHORT_CIRCUIT_INDICATOR ) {
return source.getIdentifier( entity );
}
else {
generatedId = beforeExecutionGenerator.generate( source, entity, null, INSERT );
if ( generatedId == null ) {
throw new IdentifierGenerationException( "null id generated for: " + entity.getClass() );
}
else if ( generatedId == SHORT_CIRCUIT_INDICATOR ) {
return source.getIdentifier( entity );
}
}
// TODO: define toString()s for generators
if ( LOG.isDebugEnabled() ) {
persister.setIdentifier( entity, generatedId, source );
}
final boolean delayIdentityInserts =
!source.isTransactionInProgress()
&& !requiresImmediateIdAccess
&& generatedOnExecution;
return performSave( entity, generatedId, persister, generatedOnExecution, context, source, delayIdentityInserts );
}
/**
* Generate an id before execution of the insert statements,
* using the given {@link BeforeExecutionGenerator}.
*
* @param entity The entity instance to be saved
* @param source The session which is the source of this save event.
* @param generator The entity's generator
* @param persister The entity's persister instance.
*
* @return The generated id
*/
private static Object generateId(
Object entity,
EventSource source,
BeforeExecutionGenerator generator,
EntityPersister persister) {
final Object id = generator.generate( source, entity, null, INSERT );
if ( id == null ) {
throw new IdentifierGenerationException( "Null id generated for entity '" + persister.getEntityName() + "'" );
}
else {
if ( LOG.isDebugEnabled() ) {
// TODO: define toString()s for generators
LOG.debugf(
"Generated identifier: %s, using strategy: %s",
persister.getIdentifierType().toLoggableString( generatedId, source.getFactory() ),
persister.getIdentifierType().toLoggableString( id, source.getFactory() ),
generator.getClass().getName()
);
}
return performSave( entity, generatedId, persister, false, context, source, true );
}
else {
return performSave( entity, null, persister, true, context, source, requiresImmediateIdAccess );
return id;
}
}
@ -156,13 +177,10 @@ public abstract class AbstractSaveEventListener<C>
* @param useIdentityColumn Is an identity column being used?
* @param context Generally cascade-specific information.
* @param source The session from which the event originated.
* @param requiresImmediateIdAccess does the event context require
* access to the identifier immediately after execution of this method (if
* not, post-insert style id generators may be postponed if we are outside
* a transaction).
* @param delayIdentityInserts Should the identity insert be delayed?
*
* @return The id used to save the entity; may be null depending on the
* type of id generator used and the requiresImmediateIdAccess value
* type of id generator used and on delayIdentityInserts
*/
protected Object performSave(
Object entity,
@ -171,8 +189,10 @@ public abstract class AbstractSaveEventListener<C>
boolean useIdentityColumn,
C context,
EventSource source,
boolean requiresImmediateIdAccess) {
boolean delayIdentityInserts) {
// call this after generation of an id,
// but before we retrieve an assigned id
callbackRegistry.preCreate( entity );
processIfSelfDirtinessTracker( entity, SelfDirtinessTracker::$$_hibernate_clearDirtyAttributes );
@ -191,45 +211,31 @@ public abstract class AbstractSaveEventListener<C>
LOG.tracev( "Saving {0}", infoString( persister, id, source.getFactory() ) );
}
final EntityKey key = entityKey( entity, id, persister, useIdentityColumn, source );
final EntityKey key = useIdentityColumn ? null : entityKey( id, persister, source );
if ( invokeSaveLifecycle( entity, persister, source ) ) {
return id;
}
else {
return performSaveOrReplicate(
entity,
key,
persister,
useIdentityColumn,
context,
source,
requiresImmediateIdAccess
);
return performSaveOrReplicate( entity, key, persister, useIdentityColumn, context, source, delayIdentityInserts );
}
}
private static EntityKey entityKey(Object entity, Object id, EntityPersister persister, boolean useIdentityColumn, EventSource source) {
if ( !useIdentityColumn ) {
final EntityKey key = source.generateEntityKey( id, persister );
final PersistenceContext persistenceContext = source.getPersistenceContextInternal();
final Object old = persistenceContext.getEntity( key );
if ( old != null ) {
if ( persistenceContext.getEntry( old ).getStatus() == Status.DELETED ) {
source.forceFlush( persistenceContext.getEntry( old ) );
}
else {
throw new NonUniqueObjectException( id, persister.getEntityName() );
}
private static EntityKey entityKey(Object id, EntityPersister persister, EventSource source) {
final EntityKey key = source.generateEntityKey( id, persister );
final PersistenceContext persistenceContext = source.getPersistenceContextInternal();
final Object old = persistenceContext.getEntity( key );
if ( old != null ) {
if ( persistenceContext.getEntry( old ).getStatus() == Status.DELETED ) {
source.forceFlush( persistenceContext.getEntry( old ) );
}
else if ( persistenceContext.containsDeletedUnloadedEntityKey( key ) ) {
source.forceFlush( key );
else {
throw new NonUniqueObjectException( id, persister.getEntityName() );
}
persister.setIdentifier( entity, id, source );
return key;
}
else {
return null;
else if ( persistenceContext.containsDeletedUnloadedEntityKey( key ) ) {
source.forceFlush( key );
}
return key;
}
protected boolean invokeSaveLifecycle(Object entity, EntityPersister persister, EventSource source) {
@ -255,8 +261,7 @@ public abstract class AbstractSaveEventListener<C>
* @param useIdentityColumn Should an identity column be used for id generation?
* @param context Generally cascade-specific information.
* @param source The session which is the source of the current event.
* @param requiresImmediateIdAccess Is access to the identifier required immediately
* after the completion of the save? persist(), for example, does not require this...
* @param delayIdentityInserts Should the identity insert be delayed?
*
* @return The id used to save the entity; may be null depending on the
* type of id generator used and the requiresImmediateIdAccess value
@ -268,11 +273,10 @@ public abstract class AbstractSaveEventListener<C>
boolean useIdentityColumn,
C context,
EventSource source,
boolean requiresImmediateIdAccess) {
boolean delayIdentityInserts) {
final Object id = key == null ? null : key.getIdentifier();
final boolean shouldDelayIdentityInserts = !source.isTransactionInProgress() && !requiresImmediateIdAccess;
final PersistenceContext persistenceContext = source.getPersistenceContextInternal();
// Put a placeholder in entries, so we don't recurse back and try to save() the
@ -300,7 +304,7 @@ public abstract class AbstractSaveEventListener<C>
persister,
useIdentityColumn,
source,
shouldDelayIdentityInserts
delayIdentityInserts
);
// postpone initializing id in case the insert has non-nullable transient dependencies
@ -369,7 +373,7 @@ public abstract class AbstractSaveEventListener<C>
EntityPersister persister,
boolean useIdentityColumn,
EventSource source,
boolean shouldDelayIdentityInserts) {
boolean delayIdentityInserts) {
if ( useIdentityColumn ) {
final EntityIdentityInsertAction insert = new EntityIdentityInsertAction(
values,
@ -377,7 +381,7 @@ public abstract class AbstractSaveEventListener<C>
persister,
isVersionIncrementDisabled(),
source,
shouldDelayIdentityInserts
delayIdentityInserts
);
source.getActionQueue().addAction( insert );
return insert;

View File

@ -126,16 +126,7 @@ public class DefaultReplicateEventListener
final boolean regenerate = persister.isIdentifierAssignedByInsert(); // prefer re-generation of identity!
final EntityKey key = regenerate ? null : source.generateEntityKey( id, persister );
performSaveOrReplicate(
entity,
key,
persister,
regenerate,
replicationMode,
source,
true
);
performSaveOrReplicate( entity, key, persister, regenerate, replicationMode, source, false );
}
}

View File

@ -26,29 +26,14 @@ public class DefaultSaveEventListener extends DefaultSaveOrUpdateEventListener {
protected Object performSaveOrUpdate(SaveOrUpdateEvent event) {
// this implementation is supposed to tolerate incorrect unsaved-value
// mappings, for the purpose of backward-compatibility
EntityEntry entry = event.getSession().getPersistenceContextInternal().getEntry( event.getEntity() );
final EntityEntry entry = event.getSession().getPersistenceContextInternal().getEntry( event.getEntity() );
return entry != null && entry.getStatus() != Status.DELETED
? entityIsPersistent( event )
: entityIsTransient( event );
}
protected Object saveWithGeneratedOrRequestedId(SaveOrUpdateEvent event) {
if ( event.getRequestedId() == null ) {
return super.saveWithGeneratedOrRequestedId( event );
}
else {
return saveWithRequestedId(
event.getEntity(),
event.getRequestedId(),
event.getEntityName(),
null,
event.getSession()
);
}
}
protected boolean reassociateIfUninitializedProxy(Object object, SessionImplementor source) {
if ( !Hibernate.isInitialized(object) ) {
if ( !Hibernate.isInitialized( object ) ) {
throw new PersistentObjectException("uninitialized proxy passed to save()");
}
else {

View File

@ -11,7 +11,6 @@ import org.hibernate.HibernateException;
import org.hibernate.LockMode;
import org.hibernate.PersistentObjectException;
import org.hibernate.TransientObjectException;
import org.hibernate.cache.spi.entry.CacheEntry;
import org.hibernate.classic.Lifecycle;
import org.hibernate.engine.internal.Cascade;
import org.hibernate.engine.internal.CascadePoint;
@ -88,7 +87,7 @@ public class DefaultSaveOrUpdateEventListener
}
protected Object performSaveOrUpdate(SaveOrUpdateEvent event) {
EntityState entityState = EntityState.getEntityState(
final EntityState entityState = EntityState.getEntityState(
event.getEntity(),
event.getEntityName(),
event.getEntry(),
@ -123,26 +122,21 @@ public class DefaultSaveOrUpdateEventListener
final SessionFactoryImplementor factory = event.getSession().getFactory();
Object requestedId = event.getRequestedId();
Object savedId;
final Object requestedId = event.getRequestedId();
final Object savedId;
if ( requestedId == null ) {
savedId = entityEntry.getId();
}
else {
final boolean isEqual = entityEntry.getPersister().getIdentifierType()
.isEqual( requestedId, entityEntry.getId(), factory );
if ( isEqual ) {
throw new PersistentObjectException(
"object passed to save() was already persistent: " +
MessageHelper.infoString( entityEntry.getPersister(), requestedId, factory )
);
}
savedId = requestedId;
}
if ( LOG.isTraceEnabled() ) {
@ -172,7 +166,7 @@ public class DefaultSaveOrUpdateEventListener
final EventSource source = event.getSession();
EntityEntry entityEntry = event.getEntry();
final EntityEntry entityEntry = event.getEntry();
if ( entityEntry != null ) {
if ( entityEntry.getStatus() == Status.DELETED ) {
source.forceFlush( entityEntry );
@ -182,28 +176,20 @@ public class DefaultSaveOrUpdateEventListener
}
}
Object id = saveWithGeneratedOrRequestedId( event );
source.getPersistenceContextInternal().reassociateProxy( event.getObject(), id );
return id;
}
/**
* Save the transient instance, assigning the right identifier
*
* @param event The initiating event.
*
* @return The entity's identifier value after saving.
*/
protected Object saveWithGeneratedOrRequestedId(SaveOrUpdateEvent event) {
return saveWithGeneratedId(
if ( event.getRequestedId() != null ) {
throw new AssertionFailure( "requested id should be null in save" );
}
final Object id = saveWithGeneratedId(
event.getEntity(),
event.getEntityName(),
null,
event.getSession(),
true
);
source.getPersistenceContextInternal().reassociateProxy( event.getObject(), id );
return id;
}
/**
@ -222,8 +208,8 @@ public class DefaultSaveOrUpdateEventListener
throw new AssertionFailure( "entity was persistent" );
}
Object entity = event.getEntity();
EntityPersister persister = session.getEntityPersister( event.getEntityName(), entity );
final Object entity = event.getEntity();
final EntityPersister persister = session.getEntityPersister( event.getEntityName(), entity );
event.setRequestedId( getUpdateId( entity, persister, event.getRequestedId(), session ) );
performUpdate( event, entity, persister );
}
@ -242,7 +228,7 @@ public class DefaultSaveOrUpdateEventListener
*/
protected Object getUpdateId(Object entity, EntityPersister persister, Object requestedId, SessionImplementor session) {
// use the id assigned to the instance
Object id = persister.getIdentifier( entity, session );
final Object id = persister.getIdentifier( entity, session );
if ( id == null ) {
// assume this is a newly instantiated transient object
// which should be saved rather than updated