hibernateを利用してはいけない5つのシチュエーション
2005年09月23日(金) 08:30
世の中は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アプリケーションの設計的には、接続セッションを表示層まで閉じずに持って行くことは無いでしょう。
この制限を回避するためにとる方法としてとることの出来る手法が四つあります(四つとも全然いけていませんが)。
HTMLを表示層に利用する場合は、おそらくもっとも現実的な2(この選択をした場合でも表示層の変更のために制御層を修正する必要がある)を選択することになるとおもいますが、
- Lazyローディングをしない設定にする。
- 表示層で利用する関連については、制御層(コントローラ)で先に呼び出しておく
- マッピングオブジェクトを本当のPOJOにコピーして表示層に渡す
- 関連の設定を削除する
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コミット時に更新します。もし同様の症状にはまっている場合はこちらもご覧ください。
追記ここまで
は?と思われる方は少数だと思います。上記の文章が何を意味しているのかが理解できない方の方が大多数でしょう。
次のような状況を想像してください。
このような処理を行った場合に4のタイミングで(A)の登録(あるいは更新)と共に(X)(Y)の抽出されたレコードに対してもアップデート文が発行されます。
- あるデータ(A)の更新系の処理を行うために、トランザクションを開始する。
- 必要な情報の抽出(X)やFK先(Y)の存在チェック(SQL例外に任せる人はいないでしょ?)をTransaction内で行う。
- あるデータ(A)をデータベースに登録(あるいは更新)する。
- 成功したのでトランザクションをコミットする。
抽出しただけで変更を行っていなければまだマシですが、何かの理由で情報が変更されている場合は意識しないうちに更新が行われます。
#変更を行ったとすれば、保存されるのは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を使うように言ってくる
→阻止してください
Comments
[2005年09月24日(土) 05:07]
venten
Humm
自分が休みの内にまたもや問題発生ですか。
Hibernateで起こる問題はいつまでたっても止まりませんね。月曜出社したくねえなあー・・・
>上司がhibernateを使うように言ってくる
>→阻止して下さい
笑わせてもらいました。
[2005年09月24日(土) 09:30]
makoto
あらかたやっつけたから問題ないよ。
テクノロジなんかにゃ負けね
[2005年09月24日(土) 12:42]
venten
>あらかたやっつけたから問題ないよ。
いっつも同じ事聞いてるor言ってる気がしますけどね。
その度に裏切られてるじゃないっすか・・・
月曜は躓かないでいきたいもんですね。
[2005年09月25日(日) 22:19]
ttk
>> →簡単なCMS等、複雑度を持たない物。ただ、そういった物はDjangoやRailsを使用することで、数倍の速度で作成できるでしょう。
生産性はRailsの方がよさげですが実際の実行速度はどうでしょう。
まだまだ問題は持っていますけどキャッシュ機構を持つHibernateが速いケースもあります。
たしかに複雑度を持たないサイトであればJDBCだけでSQLを書きまくってパニック状態に陥るよりはずっとマシです。
とはいえ、成熟しきっていないフレームワークですからね、ばんばん使ってフィードバックしたいところです。
[2005年09月25日(日) 23:15]
通りすがり
心の底から、賛同します。
これからHibernateを使おうとしている人には、「Hibernateを実プロダクトでまじめに使いこなそうとしたら、バッドノウハウの百貨店のような状態になるぞー」と忠告したいです。
[2005年09月26日(月) 00:53]
makoto
> 生産性はRailsの方がよさげですが実際の実行速度はどうでしょう。
実行速度を考えると、RubyやPythonを使うことが困難になります。
ただし、実行速度はある程度HWで回避できます。
人足工数(+ストレス)>HWの価格 だと考えます。
Javaは資源については不足したら足せばよいという富豪的プログラミングの先駆けだったはずですし、その場合安いのは人よりHWでしょう。
#というか、とにかく速度を!という場合はO/Rは無いでしょうし。
[2005年09月26日(月) 01:28]
た
hibernateを使わないとしたら、何を使えばいいのでしょうか?TopLink?でも、TopLinkに関する情報もそれほど多いように感じない。。
[2005年09月26日(月) 02:18]
通りすがりのプログラマ
Hibernateの使用を夢見ている,普段SQLJな人です.
現場での使用報告ありがとうございます.このような経験談は非常に貴重だと思います.
この議論の前提になっているHibernateのバージョンはいくつなのか教えていただけますか.
3.1βであるか,3.0であるかでいろいろと対策が変わってくると思います.
hibernate.orgのQueryFeaturesにはNativeSQLQueriesと書いてあります.これは3.1βのみでしょうか.
BruceTateはiBATISを推していますね.>たさん
[2005年09月26日(月) 02:57]
makoto
使っているhibernateはHibernate3.0.5です。
NativeSQLは使っていませんが、使えると思います。
#ただ、HQL/Criteria/SQLって・・・おかしくない?
やはりJavaのO/Rで実績があるのはTorqueでしょう。
Turbineの時代から使われ続けていますから。
Torqueの欠点はSQLの実行部分にPreparedStatementを使っていないことです。
#Torqueもバージョン3になってやっとまともになった感はありますが。
また、本来のO/Rマッピングという意味からするとiBATISは良さそうだなと思っています。
SQLとオブジェクトのマッピングなので変態的なことにも対応することでしょう。
[2005年09月26日(月) 05:07]
通りすがりのプログラマ
>makotoさん
ありがとうございました.
3.1になったら直っているかもしれませんね.
実はPerformanceQ&Aでもこのようなのを見つけてしまいました.
>Updating only the modified columns. Hibernate
>knows exactly which columns need updating and,
> if you choose, will update only those columns.
私はHibernateを実際に使ったことがない全くの素人です.
ですが,Hibernateのような本格的なORMには憧れています.
うまいこと育って欲しいです.
Torque,いいですね.
CayenneというものがNHLにて500万ヒット/dayの環境で使用された実績があるそうです.
これも中々良いのではないかと思います.
私の現場では今は夢ですが.(笑
[2005年09月26日(月) 06:05]
makoto
hibernateはレイヤーを間違えて思想されてしまったように思われます。
EJBが使いにくかったからとデータベースがわからない状態から作り出した(これはこれですごい)との事なので、
O/Rマッピングのレイヤーにデータベースの概念を持ち込んでしまったのでしょう。
#オブジェクトデータベースであるZODBでもこんな阿呆な事(4)はしないのに
マッピングだけにしてくれたら良かった。。。
[2005年09月27日(火) 02:46]
makoto
うはっ。2chに引用された。
ある意味オレンジニュースより嬉しいかも。
一つだけこっちに突っ込み
> 4のデータベース側にロジックって・・・
> hibernate云々の問題じゃないような気がする
これが一番hibernateを業務で使えないという部分。
selectしたデータを勝手に(しかも全件)アップデートするっていうO/Rが他にあるのかな?
[2005年09月29日(木) 02:11]
makoto
> 更新したくなければevict()すればいいのに(マリー
sessionではまったときに見つけたちゃったんだけど、これはプロジェクト全般にやらなきゃいかんなと思いつつ放置中。(evictはsessionから特定のオブジェクトを取り除くというメソッド)
#FKチェックで使ったオブジェクト全部処理すんの面倒だけど、仕方ない。。。
[2005年09月30日(金) 03:37]
た
それでは、これから、iBATISを使ってみます。
[2005年10月07日(金) 11:04]
スレ住人
> シチュエーション4は、
> 書いた奴が永続化オブジェクトを分かってないということでFA?
>
> FAくさいね。
> 後はスパムがそれを認めるかどうかだな。
> あるいは某Aのように緘黙症になるかw
だってさ。あーあw
[2005年10月07日(金) 14:17]
makoto
> > 後はスパムがそれを認めるかどうかだな。
> > あるいは某Aのように緘黙症になるかw
>
> だってさ。あーあw
確かにシチュエーション4はhibernateの使い方が間違っている(設定が出来ていない)問題臭い。
検証するからちょっとまってくだせー。
#ただし、O/Rとしてはやっぱりレイヤーを間違えているとは思いつづけている
あ、あとそのうちでいいんで教えてください。
デルモヲチブログ?
獄長さん?
FA?
さて、寝よー
[2005年10月07日(金) 15:15]
koichik
こんにちは.
こちらのエントリに触発されていろいろ書かせてもらってます.
シチュエーション 4 の検証結果にとても興味があるので,是非とも結果を公開してください.お願いします.
それから,おそらくデルモヲチブログは私のブログで,獄長は私のことを指しているのだと思われます (苦笑).
FA はファイナルアンサーらしいです.
[2005年10月09日(日) 09:48]
makoto
koichikさんの記事の一つ目はトラックバックで気づいていたんですが、
他の記事には気づいていませんでした(コメント感謝)。
読めば読むほど、そうそう、と感じます。
が、問題にしている「参照するだけで更新処理に行く」という部分は、VIEW(データベースのビュー)をCriteriaiaでlistした後、Transaction.commitするのみ(他のオブジェクトにふれさえもしていない)で例外が発生したので、
それについては原因の特定をしつつ、再検証(&記事化)という流れにしたいと思っています。
ちょいと仕事で心身が披露しているので、もう少しお待ちください。
#FAくさいけど。
Trackbacks
[2005年10月02日(日) 12:02] koichikのひとりごと
