[jpa:4] Re: Hibernate EntityManagerの使用感想

Hidenoshin Yoshida [E-MAIL ADDRESS DELETED]
2007年 4月 18日 (水) 02:27:08 JST


吉田(da-yoshi)です。

> まずは、JPAでどんなところで実際に困り、どう解決したかという
> 話があるとよいのかなと思います。
>
> あるいは、意外とここはよかったみたいなことも。

仕事でHibernate EntityManagerを使っていますので、
まずはそのお話をしてみようかなと思います。

導入のきっかけは、HibernateのListenerを使って、INSERT・UPDATE時の
共通的な処理(履歴テーブルへのINSERT等)を行いたかったからです。
ここはJPAのEventListenerではなく、HibernateのListenerの方が
細かな設定が可能だったのでそちらを選択しました。
どちらかというとHibernateのアノテーション版というイメージで
導入したので、JPAよりもHibernate色の強い利用をしています。

EntityはHibernate Toolsを使ってスキーマから自動作成し
そこに手動で、外部キー制約の無い関連定義やバージョン定義等を追加
していきました。自動作成ツールが付けたTableアノテーションやColumn
アノテーションは極力削除し、できるだけJPAの命名規約だけで定義する
ようにクラス名・フィールド名の方を変更しました。
これはJPAをよく知らない開発者に対して、アノテーションを一つ一つ
覚えるよりも「テーブルやカラムと同じ名前にしたら自動的に関連付けられる」
という規約で覚えて貰いたかったからですが、これは結構上手くいきました。

最初に大きく悩んだのは、Hibernateの双方向1対1の挙動の問題でした。
小林さんが書かれたJava ExpertのJPA特集の中でも詳しく書かれてありましたが
単純に1対1の定義をしたらLAZYロードが無効になってしまい
一覧取得等で凄まじい量のQueryが発行されてしまってかなり困りました。
結局自分の場合は、継承戦略を使って1対1の関連定義を子クラス側に定義し
1対1の関連で片方がNULLにならない状態を無理矢理作り出して凌いだのですが
今考えると、あまり良い方法ではなかったなと感じてます。。。

もう一点、悩まされたのがQueryの戻り値でした。
JPAはJPQLとSQLで同じQueryインターフェイスを利用し、
EntityManagerのcreateNamedQueryメソッドを使えば、両者の違いを意識せずに
ソースを記述できる・・・というのを売りの一つにしていますが・・・
結局、JPQLとSQLで戻り値が変わってくるので、このメリットはあまり有効では
ないことがわかってきました。

例えば、Entityではない普通のBeanを戻り値として望む場合、JPQLなら
SELECT new 句を使うと思いますが、これがSQLだと、戻り値がObject配列に
なってしまいます。
例えば、最初はJPQLで記述していた部分に対して、UNIONを使ったSELECT文に
変更する必要が発生したとき、JPQLが使えなくなった時点で戻り値が変わって
しまい、結局Daoのインターフェイスから変更する必要が生じてしまいました。
こうなると、何か変更が発生する度にそれがJPQLで対応できるかどうかで
変更内容が大きく変わってきてしまい、運用コストがかなり不安定に
なってしまいます。
そこで、SELECT文に対しては、変更の可能性が殆ど無い部分や極端に
簡単なQueryの場合を除き、最初から極力SQLを使うように心がけました。
JPAのnativeQueryは使わず、HibernateのSQLQueryを使ってSQLを発行すれば
結果をBeanに変換することが出来るので、これを利用しています。
(SQLQueryは、やろうと思えばFETCH JOIN相当のEntity結果を返すことも
可能なので、使ってみると結構便利です。)
結局、SELECT文に対しては、JPAではなくHibernateのAPIを多用して
しまう結果になってしまいましたが、現状のJPAの場合仕方ないのかなと
今は感じています。JPQLによるSELECT文の記述が減っても、Entityによる
INSERT・UPDATE処理だけで、導入メリットはかなり感じることができました。

最後に、色々悩んだのが排他制御でした。
Hibernate関連やEJB3関連のドキュメントを読むと、
最近はステートフルSessionBeanとの組合せで、ExtendedなEntityManager
を利用するパターンの紹介を見ることが多いのですが、
自分の場合はEJB環境ではなかったので、あくまでTransactionScopeの
EntityManagerのみを利用しました。
ただ、EntityManagerのmergeを行うと、更新前に必ずSELECT文が実行され
しかもCASCADE設定よっては結構大量のSELECT文が発行されるので
他にいい方法はないだろうかと色々悩みました。
しかし、Entityをロードした後に、利用者側でバージョンカラムの値を
変更できないことがわかったので、merge以外にTransactionScopeで
楽観的排他制御を実行する手段を考え付くことができず
結局、Entityを一旦Sessionに保存し、mergeを使ってUPDATEする方法に
落ち着かせました。
この部分については、JPAでのベストな手段はどうなのか
未だによくわかっていません。ただ、個人的には
ExtendedなEntityManagerはシステムの複雑性が増す気がするので
あまり第一には考えたくないと思っています。

以上、まとまりのない内容になってしまいましたが
自分がHibernate EntityManagerを使ったときの感想です。
いろんな皆さんの使用談・体験談も是非聞いてみたいなと思っています。

-- 
吉田秀之進
[E-MAIL ADDRESS DELETED]


jpa メーリングリストの案内