We recently migrated away from Session#saveOrUpdate as it is listed as deprecated, and because we wanted to depend only on the JPA API.
As such, we implemented a persist-or-merge strategy, similar to:
if (isNew(entity)) {
entityManager.persist(entity);
} else {
entityManager.merge(entity);
}
With this new implementation everything sort of works, except for some custom Hibernate event listeners.
We have some PreInsertEventListeners that validate invariants for entities, and may veto changes. Since switching to the persist-or-merge strategy, these event listeners appear to be receiving incomplete copies of the payload entity.
This apparently happens due to the way cascade interacts with DefaultMergeEventListener. The method creates a copy of the entity (1), and proceeds to copy the relation values from the parent (2). It then saves the transient (half-created) entity (3), which triggers our custom listeners. Only after this call completes will the entity be completed by copying the missing relations (4).
final Object copy = copyEntity( copyCache, entity, session, persister, id ); //1
super.cascadeBeforeSave( session, persister, entity, copyCache );
copyValues( persister, entity, copy, session, copyCache, ForeignKeyDirection.FROM_PARENT ); //2
saveTransientEntity( copy, entityName, event.getRequestedId(), session, copyCache ); //3
super.cascadeAfterSave( session, persister, entity, copyCache );
copyValues( persister, entity, copy, session, copyCache, ForeignKeyDirection.TO_PARENT ); //4
I think it is also important to mention that while Session#saveOrUpdate is deprecated, it still works for the time being, and if I switch back to it, this behavior is not observed — everything works as expected.
I’m not sure what I’m missing here. Instinctively it seems strange to me that an event listener would receive an half-completed copy of the entity as the payload, but I might be misunderstanding the APIs involved. Any ideas or clarification would be welcome!
Thank you