Hibernate: remove and add the same entity with orphanRemoval, the entity is removed

362 Views Asked by At

I have a strange behavior with Hibernate. My environment is Quarkus with Panache but I think it's not really related to the issue.

I'm using a simple tree entity with an id, parent and children. My moveInto method just moved a child into another parent:

  • I removed the child from the old parent
  • I added the child in the new parent

After the transaction, the child is completely removed and the action of the addition is not taken into account.

Is this a normal behavior?

Entity :

@Entity(name = "Element")
@Access(AccessType.PROPERTY)
@Inheritance(strategy = InheritanceType.JOINED)
public class Element {

    private Long id;
    
    private Element parent;
    
    private List<Element> subActivities = new ArrayList<>();
    
    /////////////////////////////////////////////////////////////
    /////////////////////////////////////////////////////////////
    /////////////////// Getteur Setteur /////////////////////////
    /////////////////////////////////////////////////////////////
    /////////////////////////////////////////////////////////////
    
    @Id
    @GeneratedValue
    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }
    
    @JsonManagedReference
    @OneToMany(mappedBy = "parent", fetch = FetchType.EAGER, cascade = CascadeType.ALL, orphanRemoval = true)
    public List<Element> getSubActivities() {
        return this.subActivities;
    }

    public void setSubActivities(List<Element> sousactivites) {
        this.subActivities = sousactivites;
    }
    
    @JsonBackReference
    @ManyToOne()
    @JoinColumn(name = "parent", referencedColumnName = "id")
    public Element getParent() {
        return this.parent;
    }

    public void setParent(Element parent) {
        this.parent = parent;
    }

My service :

@ApplicationScoped
public class ElementService {

    @Inject
    ElementRepository repository;

   public static Long id1;
   public static Long id2;
   public static Long id3;

   /** Prepare the test */
   @Transactional
   public void create() {
           Element element1 = new Element();
           Element element2 = new Element();
           Element element3 = new Element();
           
           this.add(element1, element3);
           
           repository.persist(element1);
           repository.persist(element2);
           repository.persist(element3);
           id1 = element1.getId();
           id2 = element2.getId();
           id3 = element3.getId();
    }

    /** Test method */
    public void test() {

        Element element1 = repository.findById(id1);
        Element element2 = repository.findById(id2);
        Element element3 = repository.findById(id3);

        System.out.println("________________ before _____________");
        System.out.println(element1 +" "+ element1.getSubActivities());
        System.out.println(element2 +" "+ element2.getSubActivities());
        
        moveInto(element2.getId(), element3.getId());


        Element element01 = repository.findById(id1);
        Element element02 = repository.findById(id2);
        Element element03 = repository.findById(id3);
        
        System.out.println("________________ after _____________");
        System.out.println(element01 +" "+ element01.getSubActivities());
        System.out.println(element02 +" "+ element02.getSubActivities());

    }

    /** Problematic method */
    @Transactional
    public void moveInto(Long parentId, Long nodeId) {
        Element parent = repository.findById(parentId);
        Element node = repository.findById(nodeId);
        moveInto(parent, node);
    }


   void moveInto(Element parent, Element tnode) {
       this.remove(tnode);
       this.add(parent, tnode);
    }
    
    void remove(Element node) {
        if (node.getParent() != null) {
            boolean deleted = node.getParent().getSubActivities().remove(node);
            node.setParent(null);
            System.out.println("isDeleted ? : " + deleted);
        }
    }

   void add(Element parent, Element value) {
        if (parent.getSubActivities() != null) {
            boolean added = parent.getSubActivities().add(value);
            value.setParent(parent);
            System.out.println("isAdded ? : " + added);
        }
    }

}

Result :

________________ before _____________
fr.projetlineaire.ganttonline.activity.test.Element@5213dcac [fr.projetlineaire.ganttonline.activity.test.Element@34699167]
fr.projetlineaire.ganttonline.activity.test.Element@44cbb4c2 []
isDeleted ? : true
isAdded ? : true
________________ after _____________
fr.projetlineaire.ganttonline.activity.test.Element@5213dcac []
fr.projetlineaire.ganttonline.activity.test.Element@44cbb4c2 []
1

There are 1 best solutions below

0
On

This could be a Hibernate bug for which you should submit a bug ticket on the issue tracker(https://hibernate.atlassian.net) with your test case(https://github.com/hibernate/hibernate-test-case-templates/blob/master/orm/hibernate-orm-5/src/test/java/org/hibernate/bugs/JPAUnitTestCase.java) that reproduces the issue.

I actually believe though, that orphan removal just doesn't work the way you would expect in this particular case. The problem might be that you are using mappedBy, but it would be best to just submit the issue and wait for the analysis of the matter by the Hibernate team.