Will removing the snapshots on the PersistantSet have any strange sideeffects?
Better do what I advised you already instead of trying to modify Hibernate internals.
Is there a better way to implement the given business process?
Yes, as already mentioned.
We recently upgraded Hibernate from 3.6.4 to 5.2.12 and I think the exceptions didn’t occure back in the old version
Hibernate 3.6 is 7 years old now. The flush operation order hasn’t changed for a very long time so this issue might be replicating on 3.6 as well. On the other hand, there are many performance improvements in 5.x that you will not find in 3.6.
thanks for the quick reply.
Unfortunately I’m not really able to fix my issue due to the unique constraint in my example and hibernate/jpa doing inserts before deletes.
Maybe I’m misunderstanding your suggestion, especially this part
delete the ones that are no longer found in the incoming collection
here’s what I did:
On the client side I added the new entries and removed the unused ones (which happen to be all of the old ones).
On the server side I just merge and flush, with the flush ending in the unique constraint exception.
Is there some way to force hibernate to do the delete first in this case? A flush before the merge doesn’t do anything obviously, due to the Hibernate not knowing about the client changes yet.
Hope you can help me out, this issue is driving me a bit nuts.
That’s where you go wrong. If you have a unique key, then you should locate the entity by that unique key and update it.
Hibernate provides the @NaturalId annotation which is backed by a unique key, In your case, the @NaturalId is exactly the column that throws the ConstraintViolationException.
So, the client sends you the new state. Instead of just calling merge on the entity, you have to:
1.Fetch the database entity along with its children
2. Compare the incoming state from the client with the one you fetched.
3. You match child entities by their @Id or by their @NaturalId. If there’s a match, then you should just update the child entity.
4. If you can’t find the child entity sent by the client, it means it’s a new child entity so you have to add it to the collection in the managed entity you fetched from the DB.
5. If there’s a child entity in the collection from the DB which can’t be found in the collection from the client, it means you can simply remove it from the collection in the managed entity (you fetched from the DB).
This is the right way to do it, and it also generates the right number of SQL statements. Deleting all entries and re-adding them back is not efficient since it unbalances and rebalances the associated B+Tree indexes.
Is there some way to force hibernate to do the delete first in this case?
Even if you can do it, it’s not efficient. You could just load the entity from the DB, clear the entity collection, flush, and re-add the child entities coming from the client. However, this is not the proper way to do it.
Whenever I try removing from PersistentSet, the item is still in the collection. Can you maybe point out why this might be the case?
That is the reason why I went with the OP’s original idea of refreshing all entities. For all the entities I had to implement Persistable to manage their isNew, as well as implement custom repositories to manage all the children, to properly overwrite them and their descendants. And do it recursively using children entities’ custom repositories. And now I’m stuck at the same issue as OP
javax.persistence.EntityNotFoundException: Unable to find project.story.model.StoryPlayCriteriaStateRequirement with id StoryPlayCriteriaStateRequirement.CompositeKey(stateId=deadbeef-0000-5773-0000-000000008003, storyPlayCriteria=StoryPlayCriteria(id=deadc0de-0000-5727-fdbc-c00000000000, version=0, isNew=true, stories=[Story(id=deadc0de-0000-5727-fdbc-000000000000, version=0, isNew=false)]))