Hibernate Criteriaクエリ setFetchMode でパフォーマンス改善 | fetch 戦略
2008/09/08 15:10Update
「Hibernate マッピング - fetch 戦略でパフォーマンス改善 | 概要」では、Hibernateのfetch 戦略が何の問題を解決するか、fetch 戦略とは何か、fetch の設定の方法、そしてサンプルからfetch戦略について学びました。本文は、HibernateのCriteriaでのfetch戦略( Criteria.setFetchMode )を学びます。
―――――――――――――――――――――――――――――――――
Hibernate マッピング - fetch 戦略でパフォーマンス改善 | 概要
―――――――――――――――――――――――――――――――――
次のような1対多関係のテーブルがあるとします。
◇FATHERテーブル
ID NAME
1 f01
2 f02
3 f03
4 f04
◇SONテーブル
id NAME FATHER_ID
1 s01 1
2 s02 1
3 s03 2
4 s04 2
5 s05 3
6 s06 3
7 s07 4
8 s07 4
◇Father.hbm.xml(抜粋)
◇テストコード(抜粋)
上のコードを実行すると、次のログが出力されます。
◇出力ログ
Hibernate: select this_.ID as ID0_, this_.NAME as NAME0_0_ from FATHER this_ where this_.NAME like ?
==========
father: id=1 name=f01
Hibernate: select sonset0_.FATHER_ID as FATHER3_1_, sonset0_.id as id1_, sonset0_.id as id0_, sonset0_.NAME as NAME1_0_, sonset0_.FATHER_ID as FATHER3_1_0_ from SON sonset0_ where sonset0_.FATHER_ID=?
son=s02
son=s01
==========
father: id=2 name=f02
Hibernate: select sonset0_.FATHER_ID as FATHER3_1_, sonset0_.id as id1_, sonset0_.id as id0_, sonset0_.NAME as NAME1_0_, sonset0_.FATHER_ID as FATHER3_1_0_ from SON sonset0_ where sonset0_.FATHER_ID=?
son=s03
son=s04
==========
father: id=3 name=f03
Hibernate: select sonset0_.FATHER_ID as FATHER3_1_, sonset0_.id as id1_, sonset0_.id as id0_, sonset0_.NAME as NAME1_0_, sonset0_.FATHER_ID as FATHER3_1_0_ from SON sonset0_ where sonset0_.FATHER_ID=?
son=s06
son=s05
==========
father: id=4 name=f04
Hibernate: select sonset0_.FATHER_ID as FATHER3_1_, sonset0_.id as id1_, sonset0_.id as id0_, sonset0_.NAME as NAME1_0_, sonset0_.FATHER_ID as FATHER3_1_0_ from SON sonset0_ where sonset0_.FATHER_ID=?
son=s07
son=s07
5件のSQLが発行してしまいますね。
◇問題
Criteriaクエリで取得したデータ(Father、Son)同時に画面に表示する場合、N+1問題が発生します。
※N+1問題:上のサンプルのように、Fatherに4件のデータがある場合、5件のSQLが発行してしまいます。
◇テストコード(抜粋)
Criteria.setFetchMode(...)メソッドでフェッチモードにFetchMode.JOINを設定します。
出力ログが次になります。
◇出力ログ
Hibernate: select this_.ID as ID2_, this_.NAME as NAME0_2_, sonset2_.FATHER_ID as FATHER3_4_, sonset2_.id as id4_, sonset2_.id as id0_, sonset2_.NAME as NAME1_0_, sonset2_.FATHER_ID as FATHER3_1_0_, father3_.ID as ID1_, father3_.NAME as NAME0_1_ from FATHER this_ left outer join SON sonset2_ on this_.ID=sonset2_.FATHER_ID left outer join FATHER father3_ on sonset2_.FATHER_ID=father3_.ID where this_.NAME like ?
==========
father: id=1 name=f01
son=s01
son=s02
==========
father: id=1 name=f01
son=s01
son=s02
==========
father: id=2 name=f02
son=s04
son=s03
==========
father: id=2 name=f02
son=s04
son=s03
==========
father: id=3 name=f03
son=s05
son=s06
==========
father: id=3 name=f03
son=s05
son=s06
==========
father: id=4 name=f04
son=s07
son=s07
==========
father: id=4 name=f04
son=s07
son=s07
SQLが1件しか発行されません。
※ここで注意いただきたいことが、そのSQL文を実行すると、次の結果になります。
フェッチモードをJOINにすると、クエリの結果リスト(fatherList)のサイズは倍になってしまいます。
Hibernate マッピング - fetch 戦略でパフォーマンス改善 | 概要
―――――――――――――――――――――――――――――――――
問題
次のような1対多関係のテーブルがあるとします。
◇FATHERテーブル
ID NAME
1 f01
2 f02
3 f03
4 f04
◇SONテーブル
id NAME FATHER_ID
1 s01 1
2 s02 1
3 s03 2
4 s04 2
5 s05 3
6 s06 3
7 s07 4
8 s07 4
◇Father.hbm.xml(抜粋)
<set name="sonSet" cascade="all" inverse="true"> <key column="FATHER_ID"/> <one-to-many class="test.array.Son"/> </set>
◇テストコード(抜粋)
public static void testFetchCriteria(Session sess) {
Criteria criteria = sess.createCriteria(Father.class);
criteria.add(Restrictions.like("name", "f%"));
List <Father> fatherList = criteria.list();
for (Father father : fatherList) {
System.out.println("==========");
System.out.println("father: id=" + father.getId() + " name=" + father.getName());
Set sonSet = father.getSonSet();
if (sonSet != null) {
Iterator ite = sonSet.iterator();
while (ite.hasNext()) {
Son son = (Son)ite.next();
System.out.println("son=" + son.getName());
}
}
}
}
上のコードを実行すると、次のログが出力されます。
◇出力ログ
Hibernate: select this_.ID as ID0_, this_.NAME as NAME0_0_ from FATHER this_ where this_.NAME like ?
==========
father: id=1 name=f01
Hibernate: select sonset0_.FATHER_ID as FATHER3_1_, sonset0_.id as id1_, sonset0_.id as id0_, sonset0_.NAME as NAME1_0_, sonset0_.FATHER_ID as FATHER3_1_0_ from SON sonset0_ where sonset0_.FATHER_ID=?
son=s02
son=s01
==========
father: id=2 name=f02
Hibernate: select sonset0_.FATHER_ID as FATHER3_1_, sonset0_.id as id1_, sonset0_.id as id0_, sonset0_.NAME as NAME1_0_, sonset0_.FATHER_ID as FATHER3_1_0_ from SON sonset0_ where sonset0_.FATHER_ID=?
son=s03
son=s04
==========
father: id=3 name=f03
Hibernate: select sonset0_.FATHER_ID as FATHER3_1_, sonset0_.id as id1_, sonset0_.id as id0_, sonset0_.NAME as NAME1_0_, sonset0_.FATHER_ID as FATHER3_1_0_ from SON sonset0_ where sonset0_.FATHER_ID=?
son=s06
son=s05
==========
father: id=4 name=f04
Hibernate: select sonset0_.FATHER_ID as FATHER3_1_, sonset0_.id as id1_, sonset0_.id as id0_, sonset0_.NAME as NAME1_0_, sonset0_.FATHER_ID as FATHER3_1_0_ from SON sonset0_ where sonset0_.FATHER_ID=?
son=s07
son=s07
5件のSQLが発行してしまいますね。
◇問題
Criteriaクエリで取得したデータ(Father、Son)同時に画面に表示する場合、N+1問題が発生します。
※N+1問題:上のサンプルのように、Fatherに4件のデータがある場合、5件のSQLが発行してしまいます。
解決策:Criteria.setFetchMode
◇テストコード(抜粋)
public static void testFetchCriteria(Session sess) {
Criteria criteria = sess.createCriteria(Father.class);
criteria.add(Restrictions.like("name", "f%"));
criteria.setFetchMode("sonSet", FetchMode.JOIN); //―――→①
...
Criteria.setFetchMode(...)メソッドでフェッチモードにFetchMode.JOINを設定します。
出力ログが次になります。
◇出力ログ
Hibernate: select this_.ID as ID2_, this_.NAME as NAME0_2_, sonset2_.FATHER_ID as FATHER3_4_, sonset2_.id as id4_, sonset2_.id as id0_, sonset2_.NAME as NAME1_0_, sonset2_.FATHER_ID as FATHER3_1_0_, father3_.ID as ID1_, father3_.NAME as NAME0_1_ from FATHER this_ left outer join SON sonset2_ on this_.ID=sonset2_.FATHER_ID left outer join FATHER father3_ on sonset2_.FATHER_ID=father3_.ID where this_.NAME like ?
==========
father: id=1 name=f01
son=s01
son=s02
==========
father: id=1 name=f01
son=s01
son=s02
==========
father: id=2 name=f02
son=s04
son=s03
==========
father: id=2 name=f02
son=s04
son=s03
==========
father: id=3 name=f03
son=s05
son=s06
==========
father: id=3 name=f03
son=s05
son=s06
==========
father: id=4 name=f04
son=s07
son=s07
==========
father: id=4 name=f04
son=s07
son=s07
SQLが1件しか発行されません。
※ここで注意いただきたいことが、そのSQL文を実行すると、次の結果になります。
| ID2_ | NAME0_2_ | FATHER3_4_ | id4_ | id0_ | NAME1_0_ | FATHER3_1_0_ | ID1_ | NAME0_1_ |
|---|---|---|---|---|---|---|---|---|
| 1 | f01 | 1 | 1 | 1 | s01 | 1 | 1 | f01 |
| 1 | f01 | 1 | 2 | 2 | s02 | 1 | 1 | f01 |
| 2 | f02 | 2 | 3 | 3 | s03 | 2 | 2 | f02 |
| 2 | f02 | 2 | 4 | 4 | s04 | 2 | 2 | f02 |
| 3 | f03 | 3 | 5 | 5 | s05 | 3 | 3 | f03 |
| 3 | f03 | 3 | 6 | 6 | s06 | 3 | 3 | f03 |
| 4 | f04 | 4 | 7 | 7 | s07 | 4 | 4 | f04 |
| 4 | f04 | 4 | 8 | 8 | s07 | 4 | 4 | f04 |
Sponsored Link
Comments
- Relative Articles
- Hibernate設定ファイルその1 - hibernate.cfg.xml - (2008/07/09 14:08)
- Hibernate c3p0 - よく利用されているコネクションプーリング - (2008/07/09 15:00)
- Hibernate 方言(Dialect)一覧 - (2008/07/09 15:10)
- Hibernate のSessionとSessionFactory - (2008/07/09 15:51)
- ThreadLocal でHibernate Session を効率的に管理する - (2008/07/09 18:43)
- Hibernateで複数のDBに接続するには? - (2008/07/09 18:47)
- Hibernateとは - (2008/07/11 14:23)
- Hibernate のインストール - (2008/07/24 11:01)
- Hibernate の開発手順 - (2008/07/24 12:44)
- Hibernate SQLをログに出力方法 - (2008/07/30 16:06)