続・Oracle TimesTen と Hibernate

今日も遊んでみました.
昨日試したサンプルでは,永続クラス FooBar が 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 アクセスに対する考え方にも影響ありそうな結果.
うひゃひゃ.