Java – hibernate bi directional many to many association creation duplication
My question is very similar to the hibernate bi directional manytomany updates with second level cache
I have class, as shown below
@Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL) @Entity public class A{ private int id; private List<B> listB; ... @Cache (usage = CacheConcurrencyStrategy.TRANSACTIONAL) @ManyToMany(cascade = {CascadeType.PERSIST,CascadeType.MERGE},targetEntity = B.class) @JoinTable(name = "A_B",joinColumns = { @JoinColumn(name = "a_id") },inverseJoinColumns = { @JoinColumn(name = "b_id") }) public List<B> getListB() { return listB ; } } @Cache (usage = CacheConcurrencyStrategy.TRANSACTIONAL) @Entity public class B{ private int id; private List<A> listA; @ManyToMany(cascade = {CascadeType.PERSIST,targetEntity = A.class) @JoinTable(name = "A_B",joinColumns = { @JoinColumn(name = "b_id") },inverseJoinColumns = { @JoinColumn(name = "a_id") }) public List<a> getListA() { return listA ; } public void addToA(A a) { a.getListB().add(this); } }
As expected for many - to - many relationships, I have been updating two aspects of a two - way relationship
Now the problem I have is that when I try to add / update items in the collection, duplicate entries pop up The following is the code I use to persist entities
b.getA().clear() ; ... ... b.getListA().add(A) ; b.addToA(A) ; em.saveOrUpdate(b) ;
The following is the query obtained by hibernate from the log
delete from A_B where b_id=? insert into A_B (b_id,a_id) values (?,?) insert into A_B (b_id,?) delete from A_B where a_id=? insert into A_B (a_id,b_id) values (?,?) insert into A_B (a_id,?)
Where was I wrong? How do I get rid of duplicates being inserted? The cache is being refreshed correctly, but duplicate entries are the only problem!
Solution
This is a classic!
The problem is that both of your mappings are owners. One should be the owner and the other should be reverse Because both are owners, changes to them will cause the database to be inserted; There is an owner and an inverse, with only one set of inserts
You should be able to rewrite B:: getlista to:
@ManyToMany(cascade = {CascadeType.PERSIST,mappedBy = "listB") public List<A> getListA()
Let everything go smoothly
Please note that only the owner is used as the @ jointable annotation Typically, any given location in a database is mapped to a location in a JPA application If you find yourself mapping twice, take a moment to see if there is a better way
By the way, you don't need the targetentity attribute; JPA providers can solve this problem from generics