【问题标题】:Hibernate inserts duplicates into a @OneToMany collectionHibernate 将重复项插入到 @OneToMany 集合中
【发布时间】:2011-12-15 18:29:48
【问题描述】:

我有一个关于 Hibernate 3.6.7 和 JPA 2.0 的问题。

考虑以下实体(为简洁起见,省略了一些 getter 和 setter):

@Entity
public class Parent {
    @Id
    @GeneratedValue
    private int id;

    @OneToMany(mappedBy="parent")
    private List<Child> children = new LinkedList<Child>();

    @Override
    public boolean equals(Object obj) {
        return id == ((Parent)obj).id;
    }

    @Override
    public int hashCode() {
        return id;
    }
}

@Entity
public class Child {
    @Id
    @GeneratedValue
    private int id;

    @ManyToOne
    private Parent parent;

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

    @Override
    public boolean equals(Object obj) {
        return id == ((Child)obj).id;
    }

    @Override
    public int hashCode() {
        return id;
    }
}

现在考虑这段代码:

// persist parent entity in a transaction

EntityManager em = emf.createEntityManager();
em.getTransaction().begin();

Parent parent = new Parent();
em.persist(parent);
int id = parent.getId();

em.getTransaction().commit();
em.close();

// relate and persist child entity in a new transaction

em = emf.createEntityManager();
em.getTransaction().begin();

parent = em.find(Parent.class, id);
// *: parent.getChildren().size();
Child child = new Child();
child.setParent(parent);
parent.getChildren().add(child);
em.persist(child);

System.out.println(parent.getChildren()); // -> [Child@1, Child@1]

em.getTransaction().commit();
em.close();

子实体被错误地插入到父实体的子列表中两次。

当执行以下任一操作时,代码可以正常工作(列表中没有重复条目):

  • 移除父实体中的mappedBy属性
  • 对子列表执行一些读取操作(例如取消注释标记为*的行)

这显然是一种非常奇怪的行为。此外,当使用 EclipseLink 作为持久性提供程序时,代码按预期工作(没有重复)。

这是一个 Hibernate 错误还是我遗漏了什么?

谢谢

【问题讨论】:

  • 能否添加 setParent 方法和 equals/hashCode 方法的代码?
  • 我刚刚添加了您要求的方法。但是,我认为这个问题与 equals / hashCode 无关。
  • 您的 equals 方法不遵守 Object.equals 的约定。此外,当 ID 生成并分配给实体时,hashCode 会发生变化。如果删除 hashCode 和 equals 后错误消失,我不会感到惊讶。顺便提一句。 Hibernate 建议不要使用 ID 来实现 equals 和 hashCode。
  • 感谢您的评论。我不知道在 equals 和 hashCode 中使用持久 id 的争议。但即使我删除了我的 equals 和 hashCode 实现,问题仍然存在。

标签: hibernate jpa-2.0 one-to-many


【解决方案1】:

这是 Hibernate 中的一个错误。奇怪了,还没报道呢,feel free to report it

针对未初始化的惰性集合的操作被排队以便在集合初始化后执行它们,当这些操作与数据库中的数据冲突时,Hibernate 不会处理这种情况。通常这不是问题,因为此队列在flush() 上被清除,并且可能的冲突更改也会在flush() 上传播到数据库。但是,某些更改(例如持久化具有由 IDENTITY 类型的生成器生成的 id 的实体,我猜这是你的情况)在没有完整 flush() 的情况下传播到数据库,在这些情况下可能会发生冲突。

作为一种解决方法,您可以在保留孩子后flush() 会话:

em.persist(child); 
em.flush();

【讨论】:

  • 感谢您的回答和解决方法!我创建了一个错误报告并参考了您的答案:HHH-6776
  • 这个bug还没解决真的很奇怪?在持久化之前需要读取对象真是太糟糕了。
  • 我在 oneToMany 中也有重复的列表。更改为 set 确实有帮助,但是,为什么我在想使用 list 时不得不使用 set?使用 hibernate 生成的 sql 查询返回 NO 重复。但是,即使查询没有返回重复项,休眠也会在列表中返回重复项。不使用 set 无法找到解决方案。我不需要使用flush,因为无状态bean ..
  • 此错误现已在 Hibernate 5.0.8 中修复:hibernate.atlassian.net/browse/HHH-5855
  • 我使用的是休眠 5.1.3,但问题仍然可以重现
【解决方案2】:

我通过告诉 Hibernate 不要在我的集合中添加重复项来解决此问题。在您的情况下,将children 字段的类型从List&lt;Child&gt; 更改为Set&lt;Child&gt;,并在Child 类上实现equals(Object obj)hashCode()

显然,这并非在所有情况下都是可能的,但如果有一种理智的方法可以确定 Child 实例是唯一的,那么这个解决方案可能相对轻松。

【讨论】:

  • 并设置@OneToMany((...), cascade = CascadeType.MERGE)
【解决方案3】:

当我遇到问题不是将项目添加到使用@OneToMany 注释的列表时,而是在尝试迭代此类列表的项目时遇到了这个问题。列表中的项目总是重复的,有时不止两次。 (在使用@ManyToMany 注释时也会发生)。在这里使用 Set 不是一个解决方案,因为这些列表应该允许其中有重复的元素。

例子:

@OneToMany(mappedBy = "parent", fetch = FetchType.EAGER)
@Cascade(CascadeType.ALL)
@LazyCollection(LazyCollectionOption.FALSE)
private List entities;

事实证明,Hibernate 使用 left outer join 执行 sql 语句,这可能导致 db 返回重复的结果。有帮助的是使用 OrderColumn 简单地定义结果排序:

@OrderColumn(name = "columnName")

【讨论】:

【解决方案4】:

通过在 Wildfly (8.2.0-Final) 中使用 Java 企业上下文(我认为它是 Hibernate 版本 4.3.7),对我来说,解决方法是,首先将子对象持久化并将其添加到惰性集合中:

...
@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public void test(){
    Child child = new Child();
    child.setParent(parent);
    childFacade.create(child);

    parent.getChildren().add(cild);

    parentFacade.edit(parent);
}

【讨论】:

    【解决方案5】:

    通过调用 empty() 方法来管理它。对于这种情况,

    parent.getChildren().isEmpty()

    之前

    parent.getChildren().add(child);
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-03-15
      • 2016-07-11
      • 2015-02-18
      • 2012-10-26
      相关资源
      最近更新 更多