2525import org .hibernate .engine .spi .PersistentAttributeInterceptor ;
2626import org .hibernate .engine .spi .SelfDirtinessTracker ;
2727import org .hibernate .engine .spi .SessionFactoryImplementor ;
28- import org .hibernate .engine .spi .SessionImplementor ;
2928import org .hibernate .event .spi .EntityCopyObserver ;
3029import org .hibernate .event .spi .EventSource ;
3130import 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 , 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,23 @@ 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+ session .getInterceptor ().preMerge ( entity , sourceValues , propertyNames , propertyTypes );
461+ final Object [] targetValues = TypeHelper .replace (
462+ sourceValues ,
463+ persister .getValues ( target ),
464+ propertyTypes ,
465+ session ,
466+ target ,
467+ copyCache
468+ );
469+ persister .setValues ( target , targetValues );
470+ session .getInterceptor ().postMerge ( entity , target , targetValues , propertyNames , propertyTypes );
429471 //copyValues works by reflection, so explicitly mark the entity instance dirty
430472 markInterceptorDirty ( entity , target );
431473 event .setResult ( result );
@@ -570,64 +612,6 @@ private static boolean existsInDatabase(Object entity, EventSource source, Entit
570612 return entry != null && entry .isExistsInDatabase ();
571613 }
572614
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-
631615 /**
632616 * Perform any cascades needed as part of this copy event.
633617 *
0 commit comments