Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
811 views
in Technique[技术] by (71.8m points)

jpa 2.0 - Hibernate inserts duplicates into a @OneToMany collection

I have a question concerning Hibernate 3.6.7 and JPA 2.0.

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;
    }
}

Now consider this piece of code:

// 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):

  • remove the mappedBy attribute in the parent entity
  • perform some read operation on the list of children (e.g. uncomment line marked by *)

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

Is this a Hibernate bug or am I missing something?

Thanks

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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

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.

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

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

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...