我正在使用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更改了数据库,因此它应该知道所有有关更改而不清除其缓存。 另一种解决方案也不行。对于我来说,为什么我应该在持续操作之后立即进行合并操作是不合逻辑的。
我认为这个问题应该有一个共同的真正解决方案。有人可以帮忙吗?