Hibernate双向多对多关联创建重复项

8
My question is pretty similar to this one Hibernate Bi-Directional ManyToMany Updates with Second Level cache I've 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, CascadeType.MERGE}, 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);
     }
}

在多对多关系中,正如预期的那样,我已经更新了双向关系的两个方面。现在我面临的问题是,在尝试添加/更新集合中的项目时会出现重复条目。以下是我用来持久化实体的代码...
b.getA().clear() ;
...
...
b.getListA().add(A) ;
b.addToA(A) ;
em.saveOrUpdate(b) ;

以下是从日志中获取的Hibernate查询语句。
delete from A_B where b_id=?

insert into A_B (b_id, a_id) values (?, ?)
insert into A_B (b_id, a_id) values (?, ?)

delete from A_B where a_id=?

insert into A_B (a_id, b_id) values (?, ?)
insert into A_B (a_id, b_id) values (?, ?)
insert into A_B (a_id, b_id) values (?, ?)
insert into A_B (a_id, b_id) values (?, ?)

我在这里做错了什么? 如何摆脱正在插入的重复项? 缓存被正确清除,但重复的条目是唯一的问题!


这个答案将帮助那些在读取时而不是写入时看到重复项的人。 - Matthew Read
1个回答

16

这是一款经典产品!

问题在于你的两个映射都是owner,而应该有一个是owner,另一个是inverse。因为两者都是owner,对任何一个进行更改都会导致插入到数据库中;而如果有一个owner和一个inverse,就只会有一组插入操作。

你应该能够将B::getListA重写为:

 @ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE}, mappedBy = "listB")
 public List<A> getListA()

并且使所有东西都能正常工作。

请注意,只有拥有方具有@JoinTable注释。通常情况下,数据库的任何给定部分在JPA应用程序中仅映射到一个位置。如果您发现自己正在对某个内容进行两次映射,请认真检查是否有更好的方法。

顺便说一句,您不需要targetEntity属性;JPA提供者可以从泛型中自动推断出它们。


3
太好了!使用“mappedBy”属性,我们告诉Hibernate通知我们哪个是所有者表和实体之间的连接配置。我的理解正确吗? - Venkat
1
你有没有想过为什么在正确设置了mappedBy之后,仍然会出现重复插入的情况?在我的情况下,我使用的是字段访问而不是方法访问。这可能是个问题吗? - JBCP
我一直在努力解决类似的问题。我的@OneToMany实际上是单向(从订单到地址)映射。最初,我使用了一个Set<Address>来存储订单的地址集合。问题是,每当向集合中添加新的有效地址时,我必须使旧地址的状态无效(即更新现有地址并持久化新的有效地址)。Hibernate会将重复数据插入到连接表中。我尝试了我能找到的所有可能的解决方案。最终,将Set<Address>更改为List<Address>解决了所有问题。 - Tom Silverman

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接