Tutorial :Saving bidirectional ManyToMany



Question:

I have two entity classes annotated in the following way

@Entity  class A {     @ManyToMany(mappedBy="A", cascade=CascadeType.ALL)     private List<B> b;   ..  }    @Entity  class B {     @ManyToMany(cascade=CascadeType.ALL)     private List<A> a;   ..  }  

If I store an instance of the class 'B', the relations are stored in the database and the getter in class 'A' will return the correct subset of B's. However, if I make changes to the list of Bs in 'A', the changes are not stored in the database?

My question is, how can I make it so that changes in either class are "cascaded" to the other class?

EDIT: I've tried different variations of removing the mappedBy-parameter and defining a JoinTable (and columns), but I've been unable to find the correct combination.


Solution:1

The shortest answer seems to be you cannot and it makes sense. In a bidirectional many-to-many association one side must be master and is used to persist changes to the underlying join table. As JPA will not maintain both side of the association, you could end up with a memory situation that could not be reloaded once stored in the database. Example:

A a1 = new A();  A a2 = new A();  B b = new B();  a1.getB().add(b);  b.getA().add(a2);  

If this state could be persisted, you would end up with the following entries in the join table:

a1_id, b_id  a2_id, b_id  

But upon loading, how would JPA know that you intended to only let b know about a2 and not a1 ? and what about a2 that should not know about b ?

This is why you have to maintain the bidirectional association yourself (and make the above example impossible to get to, even in memory) and JPA will only persist based on the state of one side of it.


Solution:2

Have you specified the inverse join columns?

@Entity  class A {     @ManyToMany(mappedBy="A", cascade=CascadeType.ALL)     private List <B> b;     ..  }    @Entity   class B {      @ManyToMany      @JoinTable (         name="A_B",         joinColumns = {@JoinColumn(name="A_ID")},         inverseJoinColumns = {@JoinColumn(name="B_ID")}     )     private List<A> a;      ..   }   

That's assuming a join table called A_B with columns A_ID and B_ID.


Solution:3

As the relationship is bi-directional so as the application updates one side of the relationship, the other side should also get updated, and be in synch. In JPA, as in Java in general it is the responsibility of the application, or the object model to maintain relationships. If your application adds to one side of a relationship, then it must add to the other side.

This can be resolved through add or set methods in the object model that handle both sides of the relationships, so the application code does not need to worry about it. There are two ways to go about this, you can either only add the relationship maintenance code to one side of the relationship, and only use the setter from one side (such as making the other side protected), or add it to both sides and ensure you avoid a infinite loop.

Source: OneToMany#Getters_and_Setters


Solution:4

Have you tryed adding the mappedBy parameter onto the A field in class B like so

@Entity  class B {     @ManyToMany(cascade=CascadeType.ALL, mappedBy = "b")     private List<A> a;   ..  }  


Solution:5

Maybe there is a small mistake in A_M's answer. In my opinion it should be:

     @Entity     class B {      @ManyToMany      @JoinTable (         name="A_B",         joinColumns = {@JoinColumn(name="B_ID")},         inverseJoinColumns = {@JoinColumn(name="A_ID")}     )     private List a;      ..   }  

Note:If u also have question or solution just comment us below or mail us on toontricks1994@gmail.com
Previous
Next Post »