在JPA + Eclipselink ManyToMany双向关系中添加所有者端对象

时间:2012-10-05 10:37:36

标签: jpa many-to-many persist bidirectional owner

我正在使用JPA和Eclipselink。

我的问题是,在@ManyToMany双向关系中,当创建另一个对象并在之前保留时,如何正确地在所有者端添加新对象。

我有两个实体类,Group和Person,它们之间存在@ManyToMany双向关系。 Group是所有者端,因此mappedBy属性在Person类中声明:

@Entity
public class Group {

    [...]

    @ManyToMany                // no mappedBy -> this is the owner side
    List<Person> persons;
}

@Entity
public class Person {
     [...]    
     @ManyToMany(mappedBy="persons")
     List<Group> groups;
}

当我首先创建Group对象以及稍后添加Persons时,一切正常。 但是,如果我首先创建并持久化Persons并稍后添加Groups,我就会遇到一些问题。这是代码:

[...]

// Creating and persisting some Person object
em.persist(new Person("Sneezy"));   // em is the EntityManager
em.persist(new Person("Happy"));

[...]

// (the two person objects are retreived from the database)
List<Person> personList = findAllPerson(); 

// Creating a new Group object
Group g = new Group("Dwarfs");

// Adding the Person objects to the new Group object
g.getPersons().add(personList.get(0));
g.getPersons().add(personList.get(1));

// Refreshing the other direction too:
personList.get(0).getGroups().add(g);
personList.get(1).getGroups().add(g);

// Persisting the new Group object
em.persist(g);

现在我可以检查数据库是否正确包含所有数据。新组位于组表中,正确的关系位于group_person连接表中。

但是,如果我对Group对象进行查询,则它包含相关的Person对象,但Person对象的group列表不包含添加它们的Group对象。所以背面方向不存在。

List<Group> groups = findAllGroup();
List<Person> persons = findAllPerson();

为什么不呢?数据库包含它的所有数据。 事实上,如果我在撤退之前清除JPA缓存,那么后退方向就在那里。所以我的问题似乎是JPA中的缓存问题。我的缓存清除代码:

em.getEntityManagerFactory().getCache().evictAll();

好的,我们想一想。我看到person类中的修改实际上并未合并(只保留了Group对象),因此JPA缓存包含没有后向关系的Person对象。好吧,让我们尝试级联来解决这个问题。 我已将CascadyType.PERSIST添加到Groups类人员属性。

    @ManyToMany(cascade=CascadeType.PERSIST)
    List<Person> persons;

但在这种情况下,新组对象的持久化会在数据库中创建人物对象的新实例,而不是使用已检索并添加到组对象的现有人员对象。

好的,我们来试试CascadeType.MERGE:

    @ManyToMany(cascade=CascadeType.MERGE)
    List<Person> persons;

在这种情况下,对新Group对象的持久操作会在后备数据库中生成正确的数据,但JPA缓存仍然包含缺少方向的Person对象。

接下来的想法是,在持久化Group对象之后,我也进行了合并操作,以在Person对象中引发级联合并。

em.persist(g);
em.merge(g);

瞧,合并操作(与Group类中的CascadeType.MERGE参数一起)解决了这个问题。

但我不喜欢缓存清除或此解决方案。 我认为只有在其他线程在后台更改数据库时才需要清除缓存。但我通过相同的EntityManager更改了数据库,因此它应该知道所有有关更改而不清除其缓存。 另一种解决方案也不行。对于我来说,为什么我应该在持续操作之后立即进行合并操作是不合逻辑的。

我认为这个问题应该有一个共同的真正解决方案。有人可以帮忙吗?

0 个答案:

没有答案