One to many JPA / Hibernate relationships removing references

I have a bi-directional relationship setup like this:

class Child{
    @ManyToOne
    @JoinTable(name = "CHILDREN_WITH_PARENT", 
            joinColumns = {@JoinColumn(name = "CHILD_ID")}, 
            inverseJoinColumns = {@JoinColumn(name = "PARENT_ID")}
    )
    private Parent parent;
}

class Parent{
    @OneToMany(mappedBy="parent", cascade=CascadeType.ALL)
    Set<Child> childrens = new HashSet<Child>();

    public void persistOrMerge() {
        EntityManager em = entityManager();
        em.getTransaction().begin();
        try {
            if (em.contains(this))
                return;
            if (id == null || id == 0) {
                this.setCreatedDate(new Date());
                em.persist(this);
            } else {
                Parent prev = em.find(Parent.class, this.id);
                if (prev == null) {
                    em.persist(this);
                } else{
                    this.setCreatedDate(new Date());
                    em.merge(this);
                }
            }
            em.flush();
            em.getTransaction().commit();

        }  finally {
            em.close();

        }
    }

}

      

On my client side, I have the following code (GWT + EntityProxy)

Set<ChildProxy> children  = new HashSet<ChildProxy>();
if(childIsNew)
   child = request.create(Children.class)
else
   child = request.edit(oldChild)
children.add(child);
//If children are deleted, they are not contained in the set
//we are sending back to server
parent.setChildren(children)
parent.persistOrMerge();

      

This code only works for adding new children. Removing children from a parent does not work even if the parent class gets a set of empty children. Links in the JOIN table are not removed.

Could you please tell me where I am missing something?

Thank!

+2


source to share


4 answers


To begin with, it's a really bad idea that an entity is one that uses the entity dispatcher directly.

The EntityManager.merge () method returns the actual merged instance, this means that in your code on release

em.merge(this)

      

You have no guarantee that the merged instance matches "this" anymore, and from now on you can see all sorts of logical problems.

If you don’t think this is so important, your problem should be resolved by enabling the removal of orphans on the OneToMany side of the relationship, assuming the children are no longer used in other relationships. Otherwise, you will have to merge manually.



@OneToMany(mappedBy="parent", cascade=CascadeType.ALL, orphanRemoval=true)
Set<Child> childrens = new HashSet<Child>();

      

The JPA 2.0 spec states that

Associations that are listed as OneToOne or OneToMany support using the orphanRemoval variant. The following actions apply when orphanRemoval is in effect:

  • If an object that is a relationship object is removed from a relationship (by setting the relationship to null or by removing the subject from the collection relationship), the delete operation will be applied to the orphaned object. The delete operation is applied during a flush operation. Orphanhood functionality is intended for privately owned persons by their parent. Portable applications should not otherwise rely on a specific deletion order and should not reassign individuals who are orphaned by another relationship or otherwise attempt to maintain it. If the orphan object is a separate, new, or deleted entity, the semantics of orphanRemoval do not apply.
  • If a delete operation is applied to a managed source object, the delete operation will be cascaded to the target according to the rules of section 3.2.3 (and therefore it is not necessary to specify cascade = CLEAR for the relationship) [20].
+6


source


// In the entity class of Parent1 write a method to unlink parent2
public void unLinkParent2(Parent2 parent2)
{
    //remove columns from the link table, pertaining to the parent2
    getParent2Collection().remove(parent2);
    // using the parent2 object remove 'this' parent1 entity link columns
    parent2.getParent1Collection().remove(this);
}

      

Parent1: Parent2: LinkP1-P2
--------------------------------------------------
Id1 (PK) Id2 (PK) Id1 (composite PK)
name1 Name2 Id2 (composite PK)


Id1

and Id2

the Link tables together represent the primary key for the Parent1

and tables Parent2

.

+1


source


0


source


The ManyToOne mapping controls the relationship - you've marked OneToMany as a mapped child relationship to its parent. This means that changes in the relationship are only picked up if they are merged from the child side. Since the children removed from the parent list are no longer there, the merge cannot be cascaded to them, and any changes to them are never saved to the database.

As others have pointed out, deleting an orphan will exclude all children not on the list when merging. This can be overkill when all you need is interdependency. In this scar, you can probably change the relationship so that it belongs to your parents. This will keep the relationship updated, but any changes to the deleted children will still not be received. The only way to get these changes is to add children to a new parent that merges, or merge them yourself. If they can exist without a parent, calling persist / merge on them directly is the best option.

0


source







All Articles