続・Oracle TimesTen と Hibernate
今日も遊んでみました.
昨日試したサンプルでは,永続クラス Foo
と Bar
が 1 対多の関係で,Foo
1 コに Bar
が 10 コ関連づけられています.
そして全ての Foo
について Bar
への関連をたどっていました.
こんな感じ.
List<Foo> list = session.createQuery(HQL).list(); for (Foo foo : list) { for (Bar bar : foo.bars) { String name = bar.name; } }
文字列定数 HQL
が
from Foo
だと Lazy フェッチ,
from Foo foo left join fetch foo.bars
だと Eager フェッチ.
Lazy フェッチの場合,内側のループで Foo
のフィールド bars
にアクセするたびに SQL が実行されます.
この場合,Foo
が 1000 コあると SQL を 1001 回実行する Lazy フェッチは SQL を 1 回しか実行しない Eager フェッチよりも 2 割ばかり遅いというのが昨日の話.
でもですよ?
もし全ての Foo
について関連をたどる必要がなかったら?
こういう 1 対多関連の場合,親側の条件によっては子を見なくていいケースってありますよね.
もちろんその条件が単純ならそれも問い合わせ条件に含めたりできますが,複雑な場合はアプリでチェックすることも多かったり.
ってことはですよ?
もし Foo
の半分は Bar
をたどらなくていいケースではどうなるだろう?
そんなわけで (どんなわけで?),こうしてみました.
List<Foo> list = session.createQuery(HQL).list(); int i = 0; for (Foo foo : list) { if (i++ % 2 == 0) { for (Bar bar : foo.bars) { String name = bar.name; } } }
これだと Lazy フェッチは SQL を 501 回実行します.Eager フェッチはもちろん 1 回.
そうしたらですね...
Lazy フェッチの方が速い.(^^;
しかも 2 割くらい速い.
Eager フェッチの場合,Bar
のインスタンスが必要かどうかにかかわらず,最初の問い合わせ時点でインスタンス化してしまいますが,Lazy フェッチだと必要がなければインスタンス化しなくて済むからってことみたい.
つ・ま・り
500 回の SQL 実行より,Bar
のインスタンスを 5000 コ (Foo
500コ× 10 コ) 生成する方がかなり重いらしい.
をいをい.
ちなみに,関連たどるのを Foo
3 コにつき 2 コにしてみたら,Lazy フェッチ (SQL 実行 667 回) と Eager フェッチ (SQL 実行 1 回) の処理時間がほぼ同じになりました.
666 回の SQL 実行と Bar
のインスタンスを 3340 コ生成するのがほぼ同等らしい.
をいをい.
永続クラスのインスタンスを (たったの) 5 コ生成するのと SQL を 1 回実行するコストがほぼ同等,と.
ものすごく単純なケースでデータも少量なのでアレですが,DB アクセスに対する考え方にも影響ありそうな結果.
うひゃひゃ.