Hibernate 将重复项插入到 @OneToMany 集合中 [英] Hibernate inserts duplicates into a @OneToMany collection

查看:30
本文介绍了Hibernate 将重复项插入到 @OneToMany 集合中的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have a question concerning Hibernate 3.6.7 and JPA 2.0.

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

Consider following entities (some getters and setters are omitted for brevity):

@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();

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

The child entity is wrongly being inserted twice into the list of children of the parent entity.

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

When doing one of the following, the code works fine (no duplicate entries in the list):

  • 移除父实体中的mappedBy属性
  • 对子列表执行一些读取操作(例如取消注释由 * 标记的行)
  • remove the mappedBy attribute in the parent entity
  • perform some read operation on the list of children (e.g. uncomment line marked by *)

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

This is obviously a very weird behavior. Also, when using EclipseLink as the persistence provider, the code works just as expected (no duplicates).

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

Is this a Hibernate bug or am I missing something?

谢谢

推荐答案

这是 Hibernate 中的一个错误.令人惊讶的是,它还没有报告,请随时报告.

It's a bug in Hibernate. Surprisingly, it's not reported yet, feel free to report it.

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

Operations against non-initialized lazy collections are queued in order to execute them after collection is initialized, and Hibernate doesn't handle the situation when these operations conflict with the data from the database. Usually it's not a problem, because this queue is cleared upon flush(), and possible conflicting changes are propagated to the database upon flush() as well. However, some changes (such as persisting of entities with ids generated by generator of type IDENTITY, I guess, it's your case) are propagated to the database without the full flush(), and in these cases conflicts are possible.

作为一种解决方法,您可以在持久化子进程后 flush() 会话:

As a workaround you can flush() the session after persisting the child:

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

这篇关于Hibernate 将重复项插入到 @OneToMany 集合中的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆