hibernate-> lazyInitializationException n:m ellation
-
29-09-2019 - |
質問
HibernateとLazyInitializationExceptionに問題があります。私は多くの答えを検索して見つけましたが、私はそれらを使用して問題を解決することはできません。
エラーが発生した場合、Junitテストを実行します。
@Test
public void testAddPerson() {
Set<Person> persons = service.getAllPersons();
// create new person
Person person = new Person();
person.setEmail("john@doe.com");
Project testProject = serviceProj.findProjectById(1);
HashSet<Project> lister = new HashSet<Project>();
lister.add(testProject);
person.setProjects(lister);
service.addPerson(person);
testProject.getPersons().add(person);
...
}
最後に表示された行:
testProject.getPersons().add(person);
このエラーをスローします:
org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.doe.john.domain.Project.persons, no session or session was closed
人とプロジェクトは双方向n:M:
person.java:
@ManyToMany(mappedBy="persons")
private Set<Project> projects = new HashSet<Project>();
project.java:
@ManyToMany
@JoinTable(name = "Project_Person",
joinColumns = {@JoinColumn(name="project_id", referencedColumnName="id")},
inverseJoinColumns = {@JoinColumn(name="person_id", referencedColumnName="id")}
)
private Set<Person> persons = new HashSet<Person>();
だから問題は何ですか?
解決
問題は、デフォルトでコレクションがゆっくりとロードされていることです。これは、アクセスするまでデータベースから実際にロードされないことを意味します。ロードするには、アクティブなセッション/トランザクションが必要です。
簡単な方法は、それをfethtype.eegerに変更することです。これにより、コレクションがすぐに入力されるようになります。
- アップデート -
私は最近、まったく同じ問題を抱えており、この種のことに対処するために実際のサービスを変更することになりました。 Projectserviceクラスでアドパーソンメソッドを宣言します。
public void addPersonTo(Project project, Person person)
{
project = em.merge(project); //EntityManager, not sure what you are using but you get the idea hopefully
project.addPerson(person);
}
他のヒント
あなたのコードをトランザクション内に入れます - これはあなたのための問題を解決します
試してみてください
person.getProjects().Add(testProject)
それ以外の
HashSet<Project> lister = new HashSet<Project>();
lister.add(testProject);
person.setProjects(lister);
そうでなければ、Hibernate Managed Collectionを吹き飛ばすので、これを行う必要があります。
Hibernateリファレンスから:
デフォルトでは、hibernate3は、コレクションの怠zyな選択フェッチと、単一値の関連性に怠zyなプロキシフェッチを使用します。これらのデフォルトは、ほとんどのアプリケーションのほとんどの関連付けにとって理にかなっています。
hibernate.default_batch_fetch_sizeを設定すると、hibernateは怠zyなフェッチにバッチフェッチ最適化を使用します。この最適化は、より詳細なレベルで有効にすることもできます。
オープンハイバーネートセッションのコンテキスト以外では、怠zyな協会へのアクセスが例外になることに注意してください。例えば:
s = sessions.openSession();
Transaction tx = s.beginTransaction();
User u = (User) s.createQuery("from User u where u.name=:userName")
.setString("userName", userName).uniqueResult();
Map permissions = u.getPermissions();
tx.commit();
s.close();
Integer accessLevel = (Integer) permissions.get("accounts"); // Error!
セッションが閉鎖されたときにアクセス許可コレクションが初期化されなかったため、コレクションは状態をロードできません。 Hibernateは、分離されたオブジェクトの怠zyな初期化をサポートしていません。これは、トランザクションがコミットされる直前にコレクションから読み取られるコードを移動することで修正できます。
または、Associationマッピングにlazy = "false"を指定することにより、非怠zyなコレクションまたはアソシエーションを使用できます。ただし、ほぼすべてのコレクションと協会に怠zyな初期化を使用することを意図しています。オブジェクトモデルで怠zyな関連性が多すぎると、Hibernateはすべてのトランザクションでデータベース全体をメモリにフェッチします。
一方、特定のトランザクションで選択する代わりに、本質的に怠zyであるJoin Fetchingを使用できます。次に、フェッチング戦略をカスタマイズする方法について説明します。 Hibernate3では、フェッチ戦略を選択するメカニズムは、単一値の関連性とコレクションと同じです。
したがって、コレクションにアクセスした後、セッションを閉じる必要があります!
それ以外の :
service.addPerson(person);
testProject.getPersons().add(person);
私はあなたが持っているべきだと思います:
testProject.getPersons().add(person);
service.addPerson(person);