I have observed a strange/unexpected behavior when trying to update an object using the JPA callback methods of another object.
The updated object is not flushed even it is explicitly merged into the persistence context. A persisted object instead is flushed as expected. In the example below, I stripped down everything from my code to give an example for the problem.
in class ExternalJob (an entity bean):
@PostUpdate
public void postUpdate() {
CDI.current().select(ExternalJobService.class).get().executePostUpdate(getId());
}
in class ExternalJobService (a stateless session bean):
public void executePostUpdate(Long id) {
// 1) newly created object of a differen class JobLog is flushed after the method has ended and thus the transaction committed
entityManager.persist(new JobLog(...));
// 2) but an update on an existing object of a different class JobLog is NOT flushed
JobLog joblog = find(JobLog.class, 11099L);
joblog.setLog(LocalDateTime.now());
entityManager.merge(joblog);
}
Is this a Hibernate bug or is my expectation is wrong? Many thanks in advance for some clarifying feedback.
By the way: I am on Hibernate Version 5.4.30.Final
Many thanks for your response. It would be a great help if somebody who is deep in Hibernate could simple tell whether there is a difference in the treatment of persisted and merged entites in the flush handling when the merge/persist is invoked via a callback method such @PreUpdate/Post-Update.
Thanks in advance.
PS: I am not sure whether I am able to deliver a reproducer (soon).
I am deep in Hibernate, but not that particular part of the code. Anyway, I just had a quick look into the code and it seems to me that this is totally unsupported and if it happens to work, then this is only by accident. If you look into org.hibernate.engine.spi.ActionQueue#executeActions(org.hibernate.engine.spi.ExecutableList<E>) where the execution happens, the ExecutableList is iterated, but the iterator does not handle that elements could be added to the list while iterating org.hibernate.engine.spi.ExecutableList#iterator.
From what I can see, it depends at which phase you interact with the EntityManager. In the best case, the changes will happen in the next flush if you flush explicitly. Otherwise, the operations would just be ignored.
dear @beikov many thanks for your very helpful answer.
would this not be valid feature request for Hibernate to ensure by default that all objects either added by persist() or merge() to the persistence context are included into the corresponding flush of the corresponding transaction - even though this issue is not finally/fully specified by the standard?
reading the following callback method description are naturally leading to expectation that they should be executed within the boundaries of the current transaction - although i am fully aware that this is not stated explicitly here.
Type
Description
PrePersist
Executed before the entity manager persist operation is actually executed or cascaded. This call is synchronous with the persist operation.
PreRemove
Executed before the entity manager remove operation is actually executed or cascaded. This call is synchronous with the remove operation.
PostPersist
Executed after the entity manager persist operation is actually executed or cascaded. This call is invoked after the database INSERT is executed.
PostRemove
Executed after the entity manager remove operation is actually executed or cascaded. This call is synchronous with the remove operation.
PreUpdate
Executed before the database UPDATE operation.
PostUpdate
Executed after the database UPDATE operation.
PostLoad
Executed after an entity has been loaded into the current persistence context or an entity has been refreshed.