我正在使用 JPA 和 Eclipselink。
我的问题是,在@ManyToMany 双向关系中,当另一方对象之前创建并保留时,如何在所有者方正确添加新对象。
我有两个实体类,Group 和 Person,它们之间存在 @ManyToMany 双向关系。Group 是所有者,因此在 Person 类中声明了 mappedBy 属性:
@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;
}
当我先创建组对象然后添加人员时,一切正常。但是,如果我首先创建并保留 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 对象。所以后面的方向不存在。
List<Group> groups = findAllGroup();
List<Person> persons = findAllPerson();
为什么不?数据库包含它的所有数据。实际上,如果我在检索之前进行 JPA 缓存清除,则反向方向就在那里。所以我的问题似乎是 JPA 中的缓存问题。我的缓存清除代码:
em.getEntityManagerFactory().getCache().evictAll();
好吧,让我们想想。我看到 person 类中的修改实际上并没有合并(只有 Group 对象被持久化),因此 JPA 缓存包含 Person 对象而没有反向关系。好的,让我们尝试级联来解决这个问题。我已将 CascadyType.PERSIST 添加到 Groups 类的 people 属性中。
@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 更改了数据库,因此它应该知道所有更改而不清除其缓存。而另一种解决方案也不行。为什么我应该在持久操作之后立即进行合并操作对我来说是不合逻辑的。
我认为这个问题应该有一个共同的真正解决方案。有人可以帮忙吗?