JPAとEclipselinkを使用しています。
私の問題は、 @ManyToMany 双方向の関係で、反対側のオブジェクトが以前に作成されて永続化されたときに、所有者側に新しいオブジェクトを正しく追加する方法です。
Group と Person の 2 つのエンティティ クラスがあり、それらの間には @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 オブジェクトを作成し、後で Person を追加すると、すべて正常に機能します。しかし、最初に Person を作成して保持し、後で Group を追加すると、問題が発生します。コードは次のとおりです。
[...]
// 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 テーブルにあり、適切なリレーションは group_person 結合テーブルにあります。
ただし、Group オブジェクトのクエリを作成すると、関連する Person オブジェクトが含まれますが、その Person オブジェクトのグループのリストには、それらが追加された Group オブジェクトが含まれません。したがって、後ろ方向はありません。
List<Group> groups = findAllGroup();
List<Person> persons = findAllPerson();
なぜだめですか?データベースには、そのすべてのデータが含まれています。実際、取得前にJPAキャッシュをクリアすると、逆方向があります。したがって、私の問題はJPAのキャッシングの問題のようです。私のキャッシュクリアコード:
em.getEntityManagerFactory().getCache().evictAll();
よし、考えよう。person クラスの変更が実際にはマージされていない (Group オブジェクトのみが保持されている) ことがわかります。そのため、JPA キャッシュには後方関係のない Person オブジェクトが含まれています。では、この問題を解決するためにカスケードを試してみましょう。Groups クラスの person プロパティに CascadyType.PERSIST を追加しました。
@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);
そして出来上がり、マージ操作 (グループ クラスの CascadeType.MERGE パラメータと一緒に) が問題を解決しました。
しかし、キャッシュのクリアもこの解決策も好きではありません。データベースが他のスレッドによってバックグラウンドで変更された場合にのみ、キャッシュをクリアする必要があると思います。しかし、同じ EntityManager を介してデータベースを変更したので、キャッシュをクリアせずにすべての変更を認識できるはずです。そして、他の解決策もOKではありません。永続化操作の直後にマージ操作を行う必要がある理由は、私にとって論理的ではありません。
この問題には共通の真の解決策があるはずだと思います。誰でも助けることができますか?