Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

orphanRemoval not working properly in hibernate : Element inserted and removed from list is persisted in DB

We have an issue that sound like a bug in Hibernate dealing with orphanRemoval=true for a oneToMany List (with index).

Here is the simplified mapping :

public class ParentClass {

  [...]

  @OneToMany(cascade = ALL, mappedBy = "parent", orphanRemoval = true)
  @OnDelete(action = OnDeleteAction.CASCADE)
  @Fetch(FetchMode.JOIN)
  @OrderColumn(name = "pos", nullable = false)
  public List<ChildClass> getChildren() {
    return children;
  }

}

And the child class :

public class ChildClass {
    [...]

    @ManyToOne
    @JoinColumn(nullable = false, name = "parent_id")
    public ParentClass getParent() {
        return parent;
    } 
}

Given this mapping, the following scenario is failing :

  1. Fetch a Parent from DB
  2. Add a child to it
  3. Get something else from DB (not same entity), producing a partial flush
  4. Remove the child from the parent
  5. Leave the transaction

Here is the code

@Transactional
public void test() {

  // 1)
  ParentClass parent = entityManager.find(ParentClass.class, "some-id");

  // 2)
  ChildClass child = new ChildClass(parent);
  parent.getChildren().add(child);

  // 3)
  entityManager.find(SomethingElse.class, "2");

  // 4)
  parent.getChildren().remove(child);  
}

In that case, the child allocation is inserted into DB and not removed at te end of the transaction.

However, if we don't do step 3), the child allocation is correctly not persisted in DB

Is that a bug ? A wrong mapping ? Is there a workaround for it ?

like image 381
Raphael Jolivet Avatar asked Mar 17 '23 03:03

Raphael Jolivet


2 Answers

Yes, that is a bug. I bet you don't use the last version of Hibernate (4.3.8), and that it is related (if not the same issue) to this issue: [HHH-9330] orphanRemoval=true does not work in bidirectional relationships (without cascading) Even if you use the last version, this bug could still exist. If so, please report it and then report the URL to the bug here somewhere. Workaround: try adding the child only when sure it must be persisted or also try setting to null the parent in the child:

child.setParent(null);

Also as another workaround you may try using Hibernate sessions, instead of EntityManager (as it is written in Hibernate's forum).

like image 174
V G Avatar answered Apr 05 '23 22:04

V G


You need to set both sides of the association when removing a Child:

child.setParent(null);
parent.getChildren().remove(child);  

The orphan-removal is not sufficient in your case. If the one-to-many side was a unidirectional association, then you could simply remove the Child from the List and the delete would propagate to the Child entity.

When you have a bi-directional association, you need to have both sides in sync. That's why you need to de-associate the Child from the Parent entity upon deletion.

like image 43
Vlad Mihalcea Avatar answered Apr 05 '23 22:04

Vlad Mihalcea