Hibernate マッピング - fetch 戦略でパフォーマンス改善 | 概要
2008/09/05 19:27Update
Hibernate fetchはLAZYロードにおけるN+1問題を解決します。
問題
Hibernateでは、パフォーマンスの低下を避けるために、LAZYロード(遅延ロード)という仕組みを提供しています。
※LAZYロードとは、関連するオブジェクトのロードを最初にアクセルされるまで遅延させることです。即ち、使われていないオブジェクトに、データベースへの問い合わせを行わないことになり、リソースの節約やパフォーマンスを向上します。
※Hibernate3からデフォルトでLAZYロードが有効になっています。
Hibernate究明 - Hibernate のLAZYロード 
そこで、問題が発生します。
例えば、ある画面の一覧に関連付けられたデータが同時に表示される場合、Hibernateは以下のように処理しています。
1)主たるオブジェクトを取得するために、DBにクエリを発行します。
2)LAZYロードと設定されているため、主たるオブジェクトと関連するオブジェクトの取得を遅延させます。
3)取得した一覧データを画面(JSPなど)に表示させます。
4)画面側がデータを一行ずつ処理するときに、初めて関連オブジェクトをアクセスするので、DBにクエリを発行します。
処理するデータが1万件あるとしたら、1万+1件のSQLをDBに発行してしまいます(N+1問題)。これはデータベースに大きな負担となり、システムのパフォーマンスを低下させる可能性があります。
この問題を解決するには、LAZYロードを無効にするか、他の解決策を探すしかありません。
LAZYロードを無効にしてしまうと、ほかの処理にパフォーマンスを低下させる恐れもあります。
Hibernate fetch戦略はこの問題を解決します。
fetchの値
fetchにselectやjoinを設定することができます。
◇select:外部結合を行いません。即ち、別々のSELECT文による関連しあうオブジェクトを取得します。
例:fetch="select"
◇join:LAZYロードせず、外部結合によるDB問い合わせを行います。
例:fetch="join"
fetchの設定
■Hibernateマッピング設定ファイル(*.hbm.xml)のmany-to-oneやmapなどの要素によって設定
◇ <many-to-one>、<many-to-many>、<one-to-one>
例:<many-to-one ... fetch="select">
◇ <map>、<set>、<bag>、<idbag>、<list>、<array>、<primitive-array>、<join>
例:<set ... fetch="select">
<set>と<many-to-many>などの要素ともにfetch属性を付加する場合、外側の<set>が優先されます。
■HQLクエリ時のfetch設定
例:
from Father f join fetch f.sonSet
HQLにjoin fetch を利用することで、外部結合による問い合わせを行います。
■Criteriaクエリ時のfetch設定
例:
List <Father> fatherList = (List<Father>)sess.createCriteria(Father.class).setFetchMode("sonSet", FetchMode.JOIN).list();
fetch サンプル
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
■fetch="join"を設定しなかった場合
◇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 testFetch(Session sess) {
List <Father> fatherList = (List<Father>)sess.createCriteria(Father.class).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_
==========
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=s04
son=s03
==========
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=s05
son=s06
==========
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
複数件のSQL文が発行されています。
■fetch="join"を指定する場合
次に、<set>にfetch="join"を指定するようにします。
◇Father.hbm.xml(抜粋)
<set name="sonSet" cascade="all" inverse="true" fetch="join"> <key column="FATHER_ID"/> <one-to-many class="test.array.Son"/> </set>
次のようなログが出力されます。
Hibernate: select this_.ID as ID1_, this_.NAME as NAME0_1_, sonset2_.FATHER_ID as FATHER3_3_, sonset2_.id as id3_, sonset2_.id as id0_, sonset2_.NAME as NAME1_0_, sonset2_.FATHER_ID as FATHER3_1_0_ from FATHER this_ left outer join SON sonset2_ on this_.ID=sonset2_.FATHER_ID
==========
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
1件のSQL文をしか発行されていません。
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)