Hibernateは初めてです。
私はユーザーグループに多対多の関係を持っています。 3つのテーブル:ユーザー、グループ、ユーザーグループのマッピングテーブル。
エンティティ:
@Entity
@Table(name = "user")
public class User {
@Id
@Column (name = "username")
private String userName;
@Column (name = "password", nullable = false)
private String password;
@ManyToMany(cascade = {CascadeType.ALL}, fetch = FetchType.EAGER)
@JoinTable(name="usergroup",
joinColumns={@JoinColumn(name="username")},
inverseJoinColumns={@JoinColumn(name="groupname")})
private Set<Group> userGroups = new HashSet<Group>();
... setter and getters
@Entity
@Table(name = "group")
public class Group {
@Id
@Column(name = "groupname")
private String groupName;
@Column(name = "admin", nullable = false)
private String admin;
@ManyToMany(mappedBy = "userGroups", fetch = FetchType.EAGER)
private Set<User> users = new HashSet<User>();
... setter and getters
グループエンティティでは、フェッチメソッドEAGERを使用していることに注意してください。ここで、DAOを呼び出して、次の基準を使用してシステム内のすべてのグループを取得します。
Criteria criteria = session.createCriteria(Group.class);
return criteria.list();
グループの実際の数を取得する代わりに、mappginテーブル(ユーザーグループ)からすべての行を取得しています...
たとえば、ユーザーテーブルにある場合
username password
-----------------
user1 user1
user2 user2
グループテーブル内
groupname admin
---------------
grp1 user1
grp2 user2
ユーザーグループテーブル内
username groupname
------------------
user1 grp1
user2 grp2
user1 grp2
user2 grp1
結果は次のリストになります-{grp1、grp2、grp2、grp1}
{grp1、grp2}の代わりに
グループエンティティでフェッチメソッドをLAZYに変更すると、正しい結果が得られますが、休止状態では別の場所でLazyExceptionがスローされます...
どのフェッチ方法を使用する必要があり、その理由を教えてください。
ありがとう!
怠惰な人は、常に_FetchType.EAGER
_を直感に反して使用するように指示します。これらは、一般的にデータベースのパフォーマンスを気にせず、開発作業を楽にすることだけを気にする人々です。パフォーマンスを向上させるには、_FetchType.LAZY
_を使用する必要があると言います。データベースアクセスは通常、ほとんどのアプリケーションのパフォーマンスのボトルネックであるため、少しでも役立ちます。
グループのユーザーのリストを実際に取得する必要がある場合、トランザクションセッション内からgetUsers()
を呼び出す限り、そのLazyLoadingException
は取得されません。すべての新しいHibernateユーザー。
次のコードは、それらのグループのユーザーのリストを入力せずに、すべてのグループを取得します
_//Service Class
@Transactional
public List<Group> findAll(){
return groupDao.findAll();
}
_
次のコードは、DAOレベルでそれらのグループのユーザーを持つすべてのグループを取得します。
_//DAO class
@SuppressWarnings("unchecked")
public List<Group> findAllWithUsers(){
Criteria criteria = getCurrentSession().createCriteria(Group.class);
criteria.setFetchMode("users", FetchMode.SUBSELECT);
//Other restrictions here as required.
return criteria.list();
}
_
編集1:このコードを提供してくれたAdrianShumに感謝します
さまざまなタイプのFetchMode
の詳細については、 ここ を参照してください。
コレクションオブジェクトにアクセスするためだけに別のDAOメソッドを作成する必要がない場合は、親オブジェクトのフェッチに使用されたのと同じSession
にいる限り、Hibernate.initialize()
子コレクションオブジェクトの初期化を強制するメソッド。親オブジェクトの_List<T>
_に対してこれを行うことはお勧めしません。それはデータベースにかなりの負荷をかけるでしょう。
_//Service Class
@Transactional
public Group findWithUsers(UUID groupId){
Group group = groupDao.find(groupId);
//Forces the initialization of the collection object returned by getUsers()
Hibernate.initialize(group.getUsers());
return group;
}
_
上記のコードを使用しなければならない状況に遭遇したことはありませんが、比較的効率的であるはずです。 Hibernate.initialize()
の詳細については、 ここ を参照してください。
これは、DAOでフェッチするのではなく、サービスレイヤーで行いました。これは、別のDAOメソッドを作成するのではなく、サービスで1つの新しいメソッドを作成するだけでよいためです。重要なことは、トランザクション内でgetUsers()
呼び出しをラップしたため、Hibernateが追加のクエリを実行するために使用できるセッションが作成されることです。これは、DAOでコレクションに結合基準を書き込むことによっても実行できますが、自分で実行する必要はありませんでした。
とはいえ、最初のメソッドよりも2番目のメソッドをはるかに多く呼び出していることに気付いた場合は、フェッチタイプをEAGER
に変更し、データベースに処理を任せることを検討してください。
JamesENLからの回答はほぼ正しいですが、いくつかの非常に重要な側面が欠けています。
彼が行っているのは、トランザクションがまだアクティブなときに、遅延読み込みプロキシを強制的に読み込むことです。 LazyInitializationエラーは解決されましたが、遅延読み込みは1つずつ実行されるため、パフォーマンスが大幅に低下します。基本的に、これはFetchType.EAGERと同じ結果を手動で達成するだけであり(さらに悪い方法では、JOINおよびSUBSELECT戦略を使用する可能性を逃したため)、パフォーマンスの懸念と矛盾することさえあります。
混乱を避けるために:LAZYフェッチタイプの使用は正しいです。
ただし、遅延読み込みの例外を回避するには、ほとんどの場合、リポジトリ(またはDAO?)に必要なプロパティをフェッチさせる必要があります。
最も非効率的な方法は、対応するプロパティにアクセスして遅延読み込みをトリガーすることです。いくつかの本当に大きな欠点があります:
より適切な方法は、1つのクエリ(またはいくつか)ですべての関連データをフェッチしようとすることです。
Spring-dataのような構文を使用した例を挙げてください(Hibernateリポジトリ/ DAOを手作りするために移植するのに十分直感的である必要があります):
interface GroupRepository {
@Query("from Group")
List<Group> findAll();
@Query("from Group g left join fetch g.users")
List<Group> findAllWithUsers();
}
結合のフェッチは、Criteria APIでも同様に簡単です(ただし、左結合しか使用できないようです)。Hibernateのドキュメントから引用されています。
List cats = session.createCriteria(Cat.class)
.add( Restrictions.like("name", "Fritz%") )
.setFetchMode("mate", FetchMode.EAGER)
.setFetchMode("kittens", FetchMode.EAGER)
.list();