hibernateを利用してはいけない5つのシチュエーション
世の中はhibernate礼賛のサイトが沢山あります。
O/Rの中ではバラ色の世界が広がっているように錯覚してしまいます。
しかし実際にマジに使用すると、ひどい目に遭う局面が沢山あります。
-- 2007/01/16 追記
ここに情報を探しにきているようなひとは、悪いこと言いませんのでhibernateの採用を中止しなさい。
利用しだしていても、今引き返した方が工数が減ります。間違いない。1年以上経った今でも、hibernateのオニモツ加減には慣れません。
-- 2007/01/16 追記ここまで
-- ここから追記
下記(特にシチュエーション4)について、再検証を行う必要がありそうです。
必ず、koichikさんのブログを併読してください。
http://d.hatena.ne.jp/koichik/20051002#1128268814
http://d.hatena.ne.jp/koichik/20051003#1128358837
http://d.hatena.ne.jp/koichik/20051004#1128448836
http://d.hatena.ne.jp/koichik/20051005#1128535243
http://d.hatena.ne.jp/koichik/20051006#1128621631
http://d.hatena.ne.jp/koichik/20051007#1128711612
再検証はもうしばらくお待ちください。
-- ここまで追記
2005/10/22 追記ここから
こちらもご覧ください。シチュエーション4はhibernateの使用方法が誤っていたために発生していたものです。
2005/10/22 追記ここまで
2005/12/22 追記ここから
シチュエーション4はhibernateの使用法が間違っていたために発生していた物ですが、
結局1つのプロジェクトコードにはhibernateバッドノウハウが満載になってしまいました。
単純な登録・変更・削除・検索を行うような物以外で使用する際には事前調査に相当の時間をかけた方がよいです。
SQL実行のタイミングがシビアなプロジェクトではつらいかも・・・
2005/12/22 追記ここまで
hibernateについての基礎知識
hibernateはJavaの世界で用いられているO/Rマッピングツールです。
マッピングファイル無しのannotationによる設定も可能で、EJB3の先取りとも言われています。
データベースとのやりとりには、SQLに似せたHQLという言語を用います。HQLはSQLのデータベース毎の方言による違いを吸収するためのものです。
また、HQLのほかにCriteriaというオブジェクト指向的な手法で条件を組み立てることも、部分的に直SQLを用いることも出来ます。
実際のデータベースのやりとり部分にはPreparedStatementを使用しますが、部分的に文字列の連結を行っています(いるように見えます)。
テーブル同士が関連を持つように、マッピングされるオブジェクトについても関連を定義することが出来ます。
関連を定義することによって、各オブジェクトから関連するオブジェクトを取り出すことが出来ます。
関連の取得(Select)は、必要時(メソッド呼び出し時)に行われます(これをLazyローディングといいます)。
シチュエーション1 表示層(ビュー)がHTMLでない場合
表示層に当たる部分がWebサービスであったり、Flash(Flex)と連携をする場合には、使用をしない方がよいでしょう。
基礎知識でふれたLazyローディングが悪さをします。
Lazyローディング自体は悪くないのですが、hibernateはデータベースを扱うコンポーネントであるが故の問題を抱えています。
それはある接続セッションで取得したオブジェクトに関連するオブジェクトの取得は、同一接続セッションを通して行わなければいけないという物です(違反をすると例外が発生します)。
接続セッションが違えばデータベースの状態が違うため思想としては間違っていないのですが、Webアプリケーションの設計的には、接続セッションを表示層まで閉じずに持って行くことは無いでしょう。
この制限を回避するためにとる方法としてとることの出来る手法が四つあります(四つとも全然いけていませんが)。
Lazyローディングをしない設定にする。
表示層で利用する関連については、制御層(コントローラ)で先に呼び出しておく
マッピングオブジェクトを本当のPOJOにコピーして表示層に渡す
関連の設定を削除する
HTMLを表示層に利用する場合は、おそらくもっとも現実的な2(この選択をした場合でも表示層の変更のために制御層を修正する必要がある)を選択することになるとおもいますが、
WebサービスやFlash(Flex)との連携では表示層に渡されるオブジェクトのパブリックなアクセサが全て呼び出されるため、2の選択肢は利用できません。
残る選択肢に関しても、1はある程度の規模のデータベースではあり得ませんし、3もリストを毎回POJOに(しかも必要な関連のオブジェクトも含め)コピーするのは考えたくもありません。
で、結局4にするのですが、hibenateでは関連のないテーブルとはJOINできなかったりします(つまり他の関連テーブルの情報を用いてレコードを抽出することが出来ません)。
これで残る選択肢は無くなりました。
2005/10/22 追記
3を選ぶべきでした。こちらもご覧ください。
追記ここまで
シチュエーション2 Torque等のCriteria系になれている場合
hibernateのCriteriaはおまけ程度です(公式ドキュメントにもそう書いてある)。
ちょっと複雑になると対処できません(Join一つとってもver2のころはaddJoinという直感的な物があったようですが、ver3のJoinは何?ま、関連を削除した時点で利用できませんが)。
Torqueでは非常に簡単にできたのに・・・と泣くことになりますので、hibernateの使用は避けましょう。
シチュエーション3 データベースの複数対応が必要ない場合
シチュエーション2から考えても、hibernateはHQLを使う物のようです。
HQLはSQLの方言を吸収するためのものですが、データベースの複数対応が必要ない場合には、大抵のSQLの方言よりひどい方言を学ぶことになります。
hibernateの使用は避けましょう。
シチュエーション4 データベース側にロジックが存在する場合
hibernateでTransactionを使用すると、同一Transaction内でセレクトした全てのオブジェクトをTransactionコミット時に勝手に更新します。
2005/10/22 追記
特定の条件でのみ同一Transaction内でセレクトした一部のオブジェクトをTransactionコミット時に更新します。もし同様の症状にはまっている場合はこちらもご覧ください。
追記ここまで
は?と思われる方は少数だと思います。上記の文章が何を意味しているのかが理解できない方の方が大多数でしょう。
次のような状況を想像してください。
あるデータ(A)の更新系の処理を行うために、トランザクションを開始する。
必要な情報の抽出(X)やFK先(Y)の存在チェック(SQL例外に任せる人はいないでしょ?)をTransaction内で行う。
あるデータ(A)をデータベースに登録(あるいは更新)する。
成功したのでトランザクションをコミットする。
このような処理を行った場合に4のタイミングで(A)の登録(あるいは更新)と共に(X)(Y)の抽出されたレコードに対してもアップデート文が発行されます。
抽出しただけで変更を行っていなければまだマシですが、何かの理由で情報が変更されている場合は意識しないうちに更新が行われます。
#変更を行ったとすれば、保存されるのはhibernateの特徴である「session内で取得したオブジェクトに対する変更は、sessionのクローズ時にデータベースに登録される」という変な仕様通りですが。
#DirtyFlagが見あたらないな、と思った時点で気づくべきでした。ありえねー
ここで恐ろしいのは、データベースのonUpdateに(自動的に更新ログをとるような)トリガーが仕掛けられていた場合です。何かしら値が変更された場合のみUpdateを行うように作り込んでいても、
データをセレクトしてしまうだけで勝手に更新されてしまうのです。
また、この自動更新は(データベースの)VIEWに対してもUpdate文が発行されます。VIEWにファンクションによってデータが生成されるカラムがあった場合には、例外が出ます(1日はまりました)。
シチュエーション5 問題にぶつかった際に自己解決出来ない場合
hibernateは礼賛の記事は多いのですが、問題点に関する記事はあまりありません(実際に使われているとは思えない位の量です)。
上記、Transactionを使用した場合にはセレクトした物が全て更新される等の想像外問題について、自分で原因を調査・回避できる自信が無いようでしたら、hibernateの使用は避けましょう。
おまけ 仕方なく使う羽目になってしまった技術者へのほんの少しの情報
Criteria#listをすると空のリストが返ってくる。?DAO#getInstance#getでMappingExceptionが発生する
→hibernate.cfgを確認してください。設定が必要です。
MiddlegenIDEにVIEWが出てこない
→middlegenのbuild.xmlにVIEWの名前を記述してください。
MiddlegenIDEでテーブルが探せない
→あきらめてgenerateしてから手でhbmを修正しましょう。
Criteria#listをするとListにとびとびにオブジェクトが入っている
→VIEWをリバースしたhbmをそのまま(あるいはPKの無いテーブル!)使用していませんか?PKが無い場合は全てのプロパティをあわせてコンポジットIDとしてhbmが作成されます(Middlegen)。
コンポジットIDはPKとしての扱いなので一部でもNullが存在するとオブジェクト自体がNullになってしまうようです(クソ)。
逆にhibernateを使用するといいなというようなシチュエーションは?
→簡単なCMS等、複雑度を持たない物。ただ、そういった物はDjangoやRailsを使用することで、数倍の速度で作成できるでしょう。
上司がhibernateを使うように言ってくる
→阻止してください
2008年4月23日水曜日
登録:
コメントの投稿 (Atom)
0 件のコメント:
コメントを投稿