Skip to content

Commit fb6087e

Browse files
committed
HHH-19535 introduce preMerge() and postMerge() methods of Interceptor
1 parent a3a1821 commit fb6087e

File tree

2 files changed

+97
-74
lines changed

2 files changed

+97
-74
lines changed

hibernate-core/src/main/java/org/hibernate/Interceptor.java

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -374,7 +374,7 @@ default void onInsert(Object entity, Object id, Object[] state, String[] propert
374374
* @param entity The entity instance being deleted
375375
* @param id The identifier of the entity
376376
* @param state The entity state
377-
* @param propertyNames The names of the entity properties.
377+
* @param propertyNames The names of the entity properties
378378
* @param propertyTypes The types of the entity properties
379379
*
380380
* @see StatelessSession#update(Object)
@@ -387,7 +387,7 @@ default void onUpdate(Object entity, Object id, Object[] state, String[] propert
387387
* @param entity The entity instance being deleted
388388
* @param id The identifier of the entity
389389
* @param state The entity state
390-
* @param propertyNames The names of the entity properties.
390+
* @param propertyNames The names of the entity properties
391391
* @param propertyTypes The types of the entity properties
392392
*
393393
* @see StatelessSession#upsert(String, Object)
@@ -399,10 +399,48 @@ default void onUpsert(Object entity, Object id, Object[] state, String[] propert
399399
*
400400
* @param entity The entity instance being deleted
401401
* @param id The identifier of the entity
402-
* @param propertyNames The names of the entity properties.
402+
* @param propertyNames The names of the entity properties
403403
* @param propertyTypes The types of the entity properties
404404
*
405405
* @see StatelessSession#delete(Object)
406406
*/
407407
default void onDelete(Object entity, Object id, String[] propertyNames, Type[] propertyTypes) {}
408+
409+
/**
410+
* Called before copying the state of a merged entity to a managed entity
411+
* belonging to the persistence context of a stateful {@link Session}.
412+
* <p>
413+
* The interceptor may modify the {@code state}.
414+
*
415+
* @param entity The entity passed to {@code merge()}
416+
* @param state The state of the entity passed to {@code merge()}
417+
* @param propertyNames The names of the entity properties
418+
* @param propertyTypes The types of the entity properties
419+
*
420+
* @since 7.1
421+
*/
422+
@Incubating
423+
default void preMerge(Object entity, Object[] state, String[] propertyNames, Type[] propertyTypes) {}
424+
425+
/**
426+
* Called after copying the state of a merged entity to a managed entity
427+
* belonging to the persistence context of a stateful {@link Session}.
428+
* <p>
429+
* Modification of the {@code sourceState} or {@code targetState} has no effect.
430+
*
431+
* @param source The entity passed to {@code merge()}
432+
* @param target The target managed entity
433+
* @param targetState The copied state already assigned to the target managed entity
434+
* @param originalState The original state of the target managed entity before assignment of the copied state,
435+
* or {@code null} if the target entity is a new instance
436+
* @param propertyNames The names of the entity properties
437+
* @param propertyTypes The types of the entity properties
438+
*
439+
* @since 7.1
440+
*/
441+
@Incubating
442+
default void postMerge(
443+
Object source, Object target,
444+
Object[] targetState, Object[] originalState,
445+
String[] propertyNames, Type[] propertyTypes) {}
408446
}

hibernate-core/src/main/java/org/hibernate/event/internal/DefaultMergeEventListener.java

Lines changed: 56 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@
2525
import org.hibernate.engine.spi.PersistentAttributeInterceptor;
2626
import org.hibernate.engine.spi.SelfDirtinessTracker;
2727
import org.hibernate.engine.spi.SessionFactoryImplementor;
28-
import org.hibernate.engine.spi.SessionImplementor;
2928
import org.hibernate.event.spi.EntityCopyObserver;
3029
import org.hibernate.event.spi.EventSource;
3130
import org.hibernate.event.spi.MergeContext;
@@ -274,7 +273,7 @@ protected void entityIsPersistent(MergeEvent event, MergeContext copyCache) {
274273
final EntityPersister persister = source.getEntityPersister( event.getEntityName(), entity );
275274
copyCache.put( entity, entity, true ); //before cascade!
276275
cascadeOnMerge( source, persister, entity, copyCache );
277-
copyValues( persister, entity, entity, source, copyCache );
276+
TypeHelper.replace( persister, entity, source, entity, copyCache );
278277
event.setResult( entity );
279278
}
280279

@@ -285,24 +284,52 @@ protected void entityIsTransient(MergeEvent event, Object id, MergeContext copyC
285284
final EventSource session = event.getSession();
286285
final String entityName = event.getEntityName();
287286
final EntityPersister persister = session.getEntityPersister( entityName, entity );
287+
final String[] propertyNames = persister.getPropertyNames();
288+
final Type[] propertyTypes = persister.getPropertyTypes();
288289
final Object copy = copyEntity( copyCache, entity, session, persister, id );
289290

290291
// cascade first, so that all unsaved objects get their
291292
// copy created before we actually copy
292293
//cascadeOnMerge(event, persister, entity, copyCache, Cascades.CASCADE_BEFORE_MERGE);
293294
super.cascadeBeforeSave( session, persister, entity, copyCache );
294-
copyValues( persister, entity, copy, session, copyCache, ForeignKeyDirection.FROM_PARENT );
295+
296+
final Object[] sourceValues = persister.getValues( entity );
297+
session.getInterceptor().preMerge( entity, sourceValues, propertyNames, propertyTypes );
298+
final Object[] copiedValues = TypeHelper.replace(
299+
sourceValues,
300+
persister.getValues( copy ),
301+
propertyTypes,
302+
session,
303+
copy,
304+
copyCache,
305+
ForeignKeyDirection.FROM_PARENT
306+
);
307+
persister.setValues( copy, copiedValues );
295308

296309
saveTransientEntity( copy, entityName, event.getRequestedId(), session, copyCache );
297310

298311
// cascade first, so that all unsaved objects get their
299312
// copy created before we actually copy
300313
super.cascadeAfterSave( session, persister, entity, copyCache );
301314

302-
copyValues( persister, entity, copy, session, copyCache, ForeignKeyDirection.TO_PARENT );
315+
// this is the second pass through on a merge op, so here we limit the
316+
// replacement to association types (value types were already replaced
317+
// during the first pass)
318+
// final Object[] newSourceValues = persister.getValues( entity );
319+
final Object[] targetValues = TypeHelper.replaceAssociations(
320+
sourceValues, // newSourceValues,
321+
persister.getValues( copy ),
322+
propertyTypes,
323+
session,
324+
copy,
325+
copyCache,
326+
ForeignKeyDirection.TO_PARENT
327+
);
328+
persister.setValues( copy, targetValues );
329+
session.getInterceptor().postMerge( entity, copy, targetValues, null, propertyNames, propertyTypes );
303330

304331
// saveTransientEntity has been called using a copy that contains empty collections
305-
// (copyValues uses `ForeignKeyDirection.FROM_PARENT`) then the PC may contain a wrong
332+
// (copyValues uses ForeignKeyDirection.FROM_PARENT) then the PC may contain a wrong
306333
// collection snapshot, the CollectionVisitor realigns the collection snapshot values
307334
// with the final copy
308335
new CollectionVisitor( copy, id, session )
@@ -382,29 +409,29 @@ protected void entityIsDetached(MergeEvent event, Object copiedId, Object origin
382409
LOG.trace( "Merging detached instance" );
383410

384411
final Object entity = event.getEntity();
385-
final EventSource source = event.getSession();
386-
final EntityPersister persister = source.getEntityPersister( event.getEntityName(), entity );
412+
final EventSource session = event.getSession();
413+
final EntityPersister persister = session.getEntityPersister( event.getEntityName(), entity );
387414
final String entityName = persister.getEntityName();
388415
if ( originalId == null ) {
389-
originalId = persister.getIdentifier( entity, source );
416+
originalId = persister.getIdentifier( entity, session );
390417
}
391418
final Object clonedIdentifier = copiedId == null
392419
? persister.getIdentifierType().deepCopy( originalId, event.getFactory() )
393420
: copiedId;
394421
final Object id = getDetachedEntityId( event, originalId, persister );
395422
// we must clone embedded composite identifiers, or we will get back the same instance that we pass in
396423
// apply the special MERGE fetch profile and perform the resolution (Session#get)
397-
final Object result = source.getLoadQueryInfluencers().fromInternalFetchProfile(
424+
final Object result = session.getLoadQueryInfluencers().fromInternalFetchProfile(
398425
CascadingFetchProfile.MERGE,
399-
() -> source.get( entityName, clonedIdentifier )
426+
() -> session.get( entityName, clonedIdentifier )
400427
);
401428

402429
if ( result == null ) {
403430
LOG.trace( "Detached instance not found in database" );
404431
// we got here because we assumed that an instance
405432
// with an assigned id and no version was detached,
406433
// when it was really transient (or deleted)
407-
final Boolean knownTransient = persister.isTransient( entity, source );
434+
final Boolean knownTransient = persister.isTransient( entity, session );
408435
if ( knownTransient == Boolean.FALSE ) {
409436
// we know for sure it's detached (generated id
410437
// or a version property), and so the instance
@@ -424,8 +451,24 @@ protected void entityIsDetached(MergeEvent event, Object copiedId, Object origin
424451
final Object target = targetEntity( event, entity, persister, id, result );
425452
// cascade first, so that all unsaved objects get their
426453
// copy created before we actually copy
427-
cascadeOnMerge( source, persister, entity, copyCache );
428-
copyValues( persister, entity, target, source, copyCache );
454+
cascadeOnMerge( session, persister, entity, copyCache );
455+
456+
final String[] propertyNames = persister.getPropertyNames();
457+
final Type[] propertyTypes = persister.getPropertyTypes();
458+
459+
final Object[] sourceValues = persister.getValues( entity );
460+
final Object[] originalValues = persister.getValues( target );
461+
session.getInterceptor().preMerge( entity, sourceValues, propertyNames, propertyTypes );
462+
final Object[] targetValues = TypeHelper.replace(
463+
sourceValues,
464+
originalValues,
465+
propertyTypes,
466+
session,
467+
target,
468+
copyCache
469+
);
470+
persister.setValues( target, targetValues );
471+
session.getInterceptor().postMerge( entity, target, targetValues, originalValues, propertyNames, propertyTypes );
429472
//copyValues works by reflection, so explicitly mark the entity instance dirty
430473
markInterceptorDirty( entity, target );
431474
event.setResult( result );
@@ -570,64 +613,6 @@ private static boolean existsInDatabase(Object entity, EventSource source, Entit
570613
return entry != null && entry.isExistsInDatabase();
571614
}
572615

573-
protected void copyValues(
574-
final EntityPersister persister,
575-
final Object entity,
576-
final Object target,
577-
final SessionImplementor source,
578-
final MergeContext copyCache) {
579-
if ( entity == target ) {
580-
TypeHelper.replace( persister, entity, source, entity, copyCache );
581-
}
582-
else {
583-
final Object[] copiedValues = TypeHelper.replace(
584-
persister.getValues( entity ),
585-
persister.getValues( target ),
586-
persister.getPropertyTypes(),
587-
source,
588-
target,
589-
copyCache
590-
);
591-
persister.setValues( target, copiedValues );
592-
}
593-
}
594-
595-
protected void copyValues(
596-
final EntityPersister persister,
597-
final Object entity,
598-
final Object target,
599-
final SessionImplementor source,
600-
final MergeContext copyCache,
601-
final ForeignKeyDirection foreignKeyDirection) {
602-
final Object[] copiedValues;
603-
if ( foreignKeyDirection == ForeignKeyDirection.TO_PARENT ) {
604-
// this is the second pass through on a merge op, so here we limit the
605-
// replacement to associations types (value types were already replaced
606-
// during the first pass)
607-
copiedValues = TypeHelper.replaceAssociations(
608-
persister.getValues( entity ),
609-
persister.getValues( target ),
610-
persister.getPropertyTypes(),
611-
source,
612-
target,
613-
copyCache,
614-
foreignKeyDirection
615-
);
616-
}
617-
else {
618-
copiedValues = TypeHelper.replace(
619-
persister.getValues( entity ),
620-
persister.getValues( target ),
621-
persister.getPropertyTypes(),
622-
source,
623-
target,
624-
copyCache,
625-
foreignKeyDirection
626-
);
627-
}
628-
persister.setValues( target, copiedValues );
629-
}
630-
631616
/**
632617
* Perform any cascades needed as part of this copy event.
633618
*

0 commit comments

Comments
 (0)