Re: hibernateを利用してはいけない5つのシチュエーション

久しぶりに Hibernate ネタ♪
オレンジニュースに紹介されていた時には興味なかったのですが,某巨大掲示板でも話題になっていたのでちょっと見てみました.
...
よー分からん.(^^;
ともあれ (JW),一つずつ見てみますか.

シチュエーション1 表示層(ビュー)がHTMLでない場合

この方は永続オブジェクトをトランザクション境界の外側でも使いたいようですね.
おいら的には
5.トランザクション外で永続オブジェクトを参照しない.
という流儀なので全く問題ないんですが.
これ,ビューだとか HTML だとかって全然関係ない話ですよね.

シチュエーション2 Torque等のCriteria系になれている場合

おいらは Torque 等の Criteria 系になれていないので該当しません (苦笑).
っていいうか,そもそも自分は Criteria あまり使わないしね.今や S2Hibernate.Dao もあることだし,いざとなれば HQL Criteria (仮) みたいな手も使えなくはないし.

シチュエーション3 データベースの複数対応が必要ない場合

HQL が SQL 方言なのは同意.でもだから何? という感じ.
まぁ,正直どうでもいいです.

シチュエーション4 データベース側にロジックが存在する場合

わざわざ日記で反応する気になったのはコレのため (笑).

hibernateでTransactionを使用すると、同一Transaction内でセレクトした全てのオブジェクトをTransactionコミット時に勝手に更新します。
は?と思われる方は少数だと思います。上記の文章が何を意味しているのかが理解できない方の方が大多数でしょう。

「は?」とは思いましたが (少数派らしい),何を意味しているのかが理解できませんでした (大多数と同じらしい).

次のような状況を想像してください。
  1. あるデータ(A)の更新系の処理を行うために、トランザクションを開始する。
  2. 必要な情報の抽出(X)やFK先(Y)の存在チェック(SQL例外に任せる人はいないでしょ?)をTransaction内で行う。
  3. あるデータ(A)をデータベースに登録(あるいは更新)する。
  4. 成功したのでトランザクションをコミットする。
このような処理を行った場合に4のタイミングで(A)の登録(あるいは更新)と共に(X)(Y)の抽出されたレコードに対してもアップデート文が発行されます。

うそーん.そんなの聞いたことないよ?
っていうか,「データ」って言葉が何を指しているのか不明瞭で上記の説明じゃどういう状況なのか分からないのですが...


ともあれ (JW),脳内補完して検証してみましょうか.
例によって (微謎),永続クラス ModelMagazine があるものとします.これらは Model から Magazine への多対 1 の関連 contract (専属契約の意) を持ちます.
指摘の問題を再現すべく,ModelMagazine を取得して,それらを関連づけるために Model を更新してコミットしてみましょうか.
テストコードの一部のみ抜粋.

        Transaction tx = session.beginTransaction();
        Magazine magazine = (Magazine) session.createQuery("from hoge.Magazine").list().get(0);
        Model model = (Model) session.createQuery("from hoge.Model").list().get(0);
        model.setContract(magazine);
        tx.commit();

こんなことをした場合に,変更を加えた Model だけでなく Magazine までもが更新される,と元記事では指摘しているのだと解釈してみます (自信なし).
そんなわけで (どんなわけで?),発行された SQL

Hibernate: select 
               magazine0_.id as id, 
               magazine0_.title as title1_ 
           from 
               Magazine magazine0_
Hibernate: select 
               model0_.id as id, 
               model0_.firstName as firstName0_, 
               model0_.lastName as lastName0_, 
               model0_.contract as contract0_ 
           from 
               Model model0_
Hibernate: update 
               Model 
           set 
               firstName=?, 
               lastName=?, 
               contract=? 
           where 
               id=?

Model に対する UPDATE 文だけで Magazine に対する UPDATE 文は発行されていませんが?
指摘はこういうことじゃないのかな?
いずれにせよ,

hibernateでTransactionを使用すると、同一Transaction内でセレクトした全てのオブジェクトをTransactionコミット時に勝手に更新します。

なんてことが無条件に起きるわけではないですね.


もしかしてもしかすると,Modelcontract カラムしか更新していないのに,変更していない firstNamelastName まで更新されていることを指摘してるのでしょうか?
日本語的にはそう読むのは無理な気がしますが,もしそれが気になるならマッピングファイルで

    <class name="Model" dynamic-update="true">

というように,<class> 要素の dynamic-update 属性に true を指定することで,変更のあったカラムのみ更新されるようになります.
実際に発行された SQL

Hibernate: select 
               magazine0_.id as id, 
               magazine0_.title as title1_ 
           from 
               Magazine magazine0_
Hibernate: select 
               model0_.id as id, 
               model0_.firstName as firstName0_, 
               model0_.lastName as lastName0_, 
               model0_.contract as contract0_ 
           from 
               Model model0_
Hibernate: update 
               Model 
           set 
               contract=? 
           where 
               id=?


そんなわけで (どんなわけで?),いったい何が問題だと指摘しているのかよく分からないのですが...
うーみゅ.

抽出しただけで変更を行っていなければまだマシですが、何かの理由で情報が変更されている場合は意識しないうちに更新が行われます。
#変更を行ったとすれば、保存されるのはhibernateの特徴である「session内で取得したオブジェクトに対する変更は、sessionのクローズ時にデータベースに登録される」という変な仕様通りですが。

「変な仕様」ってことはないと思いますけどね.
多くの ODB (オブジェクトデータベース) や EJB3 だって同じです.知らないけど CayenneWebObjects の EOF だってきっと同じでしょう.
元記事の著者の方,「永続オブジェクト」という認識が欠如しているように思えますね.永続オブジェクトを更新するということは,その永続状態を更新することなのだということが分かっていないように思われます.
シチュエーション1も根本的には永続オブジェクトを非永続オブジェクト (transient object) と区別していないことから来ている問題だと思いますし.

シチュエーション5 問題にぶつかった際に自己解決出来ない場合

これはそうでしょうね.Hibernate に限らず.


ともあれ (JW),「シチュエーション4 データベース側にロジックが存在する場合」が何を言っているのかよく分かっていないので,どんな状況でどんな現象が起きるのか,サンプル付きでの説明希望.