2

Originally this problem/bug posted on Hibernate's JIRA: https://hibernate.atlassian.net/browse/HHH-12311

My log properties are set to:

logging.level.org.hibernate.SQL: TRACE
logging.level.org.hibernate.event.internal: TRACE
logging.level.org.hibernate.engine.spi.CollectionEntry: TRACE
logging.level.org.hibernate.engine.spi: TRACE
logging.level.org.hibernate.engine.internal: TRACE

I have two entities: Job and Step Job has a oneToMany relationship with Step which is marked with orphanRemoval = True.

@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
@JoinTable(name = "JOB_STEP")
private List<Step> steps;

Expected behavior is when a certain step is removed from steps list, DELETE queries should be executed on STEP and JOB_STEP tables.


First case, the expected behavior occurs when following code is executed:

Job job = new Job("Test");
Step step = new Step("Test");
job.addStep(step);
repository.save(job);
job.removeStep(step);
repository.save(job);

I see three expected log messages:

o.h.e.i.AbstractFlushingEventListener: Flushed: 0 insertions, 0 updates, 1 deletions to 2 objects
org.hibernate.SQL: delete from job_step where job_id=?
org.hibernate.SQL: delete from step where id=?

Second case, when orphanRemoval = true doesn't invoke the deletion of orphans from the DB.

Job job = new Job("Test");
Step step = new Step("Test");
repository.save(job); //This causes the bug to happen
job.addStep(step);
repository.save(job);
job.removeStep(step);
repository.save(job);

After the above code is executed, I can still see entries in STEP and JOB_STEP tables in the DB.

Following log is printed: Flushed: 0 insertions, 0 updates, 0 deletions to 2 objects


I've been trying to find the root cause of this problem for few weeks now through debugging the Hibernate's source code. But it didn't really help. However, I noticed few abnormal things that might be helpful for you:

1) During debugging, I stumbled upon this method resetStoredSnapshot from org.hibernate.engine.spi.CollectionEntry that essentially just wipes the stored snapshot of Steps that later on causes Hibernate not to find orphans. While in the first case, when Hibernate works as expected, resetStoredSnapshot is not executed at all.

2) I noticed that java.util.List of Steps starts working aberrantly once it gets converted to org.hibernate.collection.PersistentList by Hibernate


The workspace with reproduced bug is here: https://github.com/yeralin/ReproduceBug

yeralin
  • 1,357
  • 13
  • 24
  • for a 50 pt bounty, I will have a look. Basically, you want it to execute deletes for the case you outline or explain why it can't/isn't done? – Christian Bongiorno Feb 25 '18 at 01:07
  • I had a similar question a while back but never got to the root cause. https://stackoverflow.com/questions/28487796/hibernate-delete-doesnt-cascade – Ayman Feb 25 '18 at 04:34
  • the children must be removed while the entity is managed, if you remove the children outside the persistence context (in client for example / front end or so) , then you tried to save the parent with the newly removed children , the children wont be removed ... so I don't know if `job.removeStep(step);` is inside the Persistence context or nor – osama yaccoub Feb 26 '18 at 16:15

2 Answers2

2

According to the doc on save:

Use the returned instance for further operations as the save operation might have changed the entity instance completely.

You should do this:

job = repository.save(job);

step may also be different after the save.

To remember this, think about the @Id. When you save an entity, you get potentially a new object with an updated id. Same with the children like step.

Syl
  • 2,733
  • 2
  • 17
  • 20
0

First of all, I don't know why you have separate table for OnToMany relationship.You can implement One To Many relationship with jobID as one column inside steps table.

To implement OnToMany relationship properly please follow https://vladmihalcea.com/the-best-way-to-map-a-onetomany-association-with-jpa-and-hibernate/.

Now to answer your question:

To remove orphan you need to set its parent to null, if you have Bi-Directional relationship
Something like

job.removeStep(step);
step.setJob(null);

Also have a look at this answer too https://stackoverflow.com/a/3071125/3425489

Shantaram Tupe
  • 1,646
  • 3
  • 16
  • 42