Hibernate 入門記 パフォチュー その1 コレクション

ごめんなさい( -_-)
更新してませんでした…。
ちがうの、台風が来てたの。
まぁ言い訳ですが。(ここまで © 眞鍋かをり)
なんか,男の言い訳って全然気持ちよくありませんね.(©エビちゃん)
ぐはぁっ.


そんなわけで (どんなわけで?),今日はやるよ,入門記!!
ついに「Chapter 14. Improving performance」に突入です.チューボーですよ! じゃなくて,パフォチューですよ!


ともあれ (JW),まずは「14.1. Understanding Collection performance」です.
Hibernate でパフォーマンスの鍵を握るのはコレクションです.そうに決まっています.そんなわけで (どんなわけで?),まずはコレクションです.


ということで「14.1.1. Taxonomy」へ.
Hibernate のコレクションは,次のように分類できるとか.

  • 値型のコレクション
  • 1対多の関連
  • 多対多の関連

後の二つはエンティティへの関連です.
でも,この分類だとテーブルや外部キーなど,リレーショナルモデル側の情報が分からないとか何とか.だったら分類するなよ.
そんなわけで (どんなわけで?),パフォーマンス的な側面も含めてコレクションを理解するには,Hibernate が行を更新または削除する際に使用する主キーも含めて理解しなきゃいけなくて,そのためには次のように分類した方がよさげらしい.

  • インデックス付きコレクション (リスト,配列,マップ)
  • セット
  • バッグ

インデックス付きのコレクションは,<key> 要素または <index> 要素で示される「主キー」を持っているので,とっても効率的だそうです.
これ,ちょっと分かりにくく感じたのですが,まずここで話題になっている「更新」や「削除」は,関連端となるオブジェクトの更新・削除ではなく,関連そのものの更新・削除だと思われます.その場合,外部キーを含んだ行を更新または削除するわけですが,その行を特定するために使うことが出来るカラムがあるかどうかが重要で,ここではそれを「主キー」と呼んでいるようです.それがテーブル定義上の主キーと同じである必要はないみたい.
セットもまた,<key> 要素で示されるカラムがあります.値型のセットの場合,<key> 要素で示されるカラムと,<element> 要素で示されるカラムとを「主キー」とすることが出来ます.しかし,<composite-element> 要素を使う場合や,<element> 要素でも LOB を使った場合などは,効率的ではないかもしれないとか.<one-to-many><many-to-many> を使う場合には十分に効率的らしいです.
バッグは一番効率がよろしくないそうです.バッグは値の重複が許されるため,インデックスを張ることが出来るカラムがありません.そこで,Hibernate はバッグに含まれる全ての行を削除し (一回の DELETE 出行われる),その後新しい行を INSERT するそうです.どひゃー.なので,とっても非効率的.うーみゅ,考えたくもないぞ,そんなこと.
要するに,バッグは使うな,<set><element><composite-element> を使う場合は (RDB の) インデックスが有効か注意しろ,って感じかなぁ.


ともあれ (JW),次は「14.1.2. Lists, maps and sets are the most efficient collections to update」.
なんでも,インデックス付きのコレクションは,多対多や値型のセットよりも効率的なのだとか.というのも,Hibernate はコレクションの要素が「変更」されても, UPDATE ではなく INSERT および DELETE をするらしいです.ここでの「変更」は,要素のプロパティに対する変更ではなく,コレクションに含まれているある要素を別の要素に変更する,ということだと思われます.その場合,例えば多対多の場合は関連テーブルを UPDATE すればいいわけですが,Hibernate は関連テーブルから元の要素との関連を示す行を DELETE して,新たな要素への関連を示す行を INSERT するということらしいです.ふむふむ.
ということは,インデックス付きのコレクションや一対多のセットの場合は UPDATE が使われるということですね.
そんなわけで (どんなわけで?),もっとも効率のよいコレクションはリスト,マップ,そしてセット (1対多) ということになるそうです.配列は,遅延初期化が出来ないので除外らしい.
でもでも,実は <idbag> なるものがあり,そいつはバッグのセマンティックスを持ちながら,他のコレクションよりも効率的だとか何とか.<idbag>... どんなんだっけ? 忘れちったよ.ごそごそ.おー,「コレクション その10 idbagmany-to-many」でしっかり学習してますね.さすが俺.っていうか,覚えてないなんてさすが俺.
それでその <idbag> ですが,そうか,関連テーブルの主キーとして,二つの外部キーからなる複合キーではなく,代理キーを使ってマッピングするってやつですね.なるほど,それはいかにも効率的.でも,そもそもバッグを使うことがあまりなさそうなので,どうでもよさげ.


次は「14.1.3. Bags and lists are the most efficient inverse collections」です.
もはやバッグは不要,永遠に忘れてしまえ! と思ったら,状況によってはバッグの方がセットよりも効率的な場合があるのだとか.
それは,コレクションが双方向 (inverse="true") な場合.
リファレンスの例を引用すると...

Parent p = (Parent) sess.load(Parent.class, id);
    Child c = new Child();
    c.setParent(p);
    p.getChildren().add(c);  //no need to fetch the collection!
    sess.flush();

ここで,p.getChildren() で返されるコレクションがバッグ (またはリスト) の場合,それへの add(c) は常に成功しますが,これがもしセットだと,要素の重複をチェックするために RDB からフェッチしなきゃいけないとのこと.うーみゅ.
そんなわけで (どんなわけで?),コレクションがバッグまたはリストの場合はコレクションを最後までフェッチしないで要素を追加できますが,セットだとそうもいかないので,双方向の関連に限ってはセットよりもバッグを使う方がいいかもしれないとのことなんですが... これは効率だけで選べる話ではないような.


そして次は「14.1.4. One shot delete」.
今度はコレクションから要素を削除 (関連を取り除く) した場合のお話.
基本的に,Hibernate はコレクションから要素を削除すると,要素一つ一つに対する DELETE を発行するようです.
ただし,コレクションが空になった場合は除く.
例えば 20 の要素を持つコレクションに対して,新しい要素を一つ追加して,既存の要素を二つ削除した場合,一つの INSERT と二つの DELETE が実行されるということらしいです.
それなら悪くなさそうですが,要素を 18 個削除した場合でも,18 回の DELETE が実行されると考えるとちょっといやーん.
そんな場合は,残したい要素二つだけからなる新しいコレクションを永続オブジェクトに設定するのがいいらしい.そうすると,一回の DELETE で 20 行を削除して,それから 2 回の INSERT が実行されるようになるらしいです.結構大変...


14.1. Understanding Collection performance」はこんな感じなんですが,お試しは... どうしようかなぁ.
やるとしたら... なんだ? バッグに項目を追加した際に,全部 DELETE してから INSERT することの確認とか,削除でちまちま DELETE されることの確認とか?
って,いうじゃなーい.
なんか,やる気しませんから!! 残念!!!!
お試しコーナーさぼり斬り!!


今週はさぼりまくりだなぁ.心より恥じる.m(__)m