JPAの問題点

JPAには非常に期待している人も多いでしょう。私もその一人です。実際にプロジェクトで使ってみて、見えてきた問題点を書いてみます。JPAの実装としては、Hibernate3.2を使っています。

  • 学習コストが高い。
    • JPAの全機能のうち、プロジェクトで使うものに絞り込んで教育すると、3日程度で教えることができるのですが、そこそこ使えるようになるには、2〜4週間かかります。これは、Hibernate in Actionにも書いているのでそういうものなんでしょう。
  • トラブルシューティングが難しい。
    • 多くのプロジェクトで実際にハマルのはこれでしょう。うちのプロジェクトでは、Hibernate職人である小林さんがいるにもかかわらずいろいろ苦労しました。Hibernate職人のいないプロジェクトで使うのは厳しいのではないかと思います。
  • SQLの扱いが貧弱。
    • JPQLは、SQLのかなり貧弱なサブセットなので、SQLを使う必要がある場合もいろいろあるのですが、JPASQLの扱いは貧弱です。SQLの結果を単独のEntityにつめて返す場合はいいのですが、複数のEntityを返そうとするとObjectの配列に個々のEntityがつめられて返ってくるので、Employee emp = (Employee) obj[0];のような感じでキャストして使うのです。それってあんまりだよね。最悪は、Entity以外にマッピングする場合で、なんとObjectの配列で返ってくるのです。Long id = (Long) obj[0];String ename = (String) obj[1];なってやる必要があるなんて。超あんまり。KuinaDaoは、S2DaoのようにSQLの結果をDTOマッピングできるので、このケースは大丈夫なのですが、素のJPAを使うときっと困るではないかと思います。
  • 新規システム以外で使うのは難しい。
    • 実際の開発では、本当に新規システムということはあまりなく、既にあるシステムを作り直すケースが多いものです。そのような場合、既にテーブルやSQLは存在するのですが、それをJPAベースに置き換えるのは、大変です。特にSQLをJPQLに変更することと、レガシーなシステムでidが使われていない場合など、かなり大変。
  • パフォーマンスチューニングが後から発生する。
    • JPAを使う場合は、Entity間の関連はlazyにするのが基本です。そうすると最初に少量のデータで開発していたときは問題が起こらないのですが、結合テストのフェーズになって本番に近いデータを扱うようになるとlazy loadingによるN+1 SELECT問題が発生し、パフォーマンスが劣化します。Fetch Joinで対応できればまだ楽なのですが、そうは行かない場合、SQLを書く必要があり、前に書いたSQLの扱いが貧弱な問題にぶち当たります。
  • View層におけるlazy loading問題
    • View層でEntityを直接扱うと、View層でEntityの関連をたどる必要があり、そこでlazy loading(SELECT文の実行)が起こる可能性があります。TransactionスコープなEnityManagerを使う場合、View層でトランザクションを制御する必要があります。それってあんまりじゃねぇ。Seasar2の場合は、S2Dxoを使ってスマートにこの問題を解決できます。
  • 立ち上げ時の初期化が遅い
    • テストするときに、JPAの環境をテストごとに初期化するとあまりに遅くやってられません。Seasar2ではテスティングフレームワーク側で対応していますが、そういうのがないとテストをするのが大変です。
  • 生産性がS2Daoより悪い
    • 既存のSQLを利用できるような開発だと、圧倒的にS2Daoの方が、生産性が上がります。新規に開発する場合でも、JPAの方が複雑なため、トラブルケースが多く意外とコストがかかります。また、書かなければいけないコードの量もJPAの方が圧倒的に多くなります。KuinaDaoを使えば、書かなければいけないコードの量はS2Dao並に少なくなるのですが、複雑だという問題は解決できません。

JPAを使って最も楽をするには、StatefulSessionBeanとExtendedなEntityManagerを使うことです。View層のlazy loadingの問題もないし、detachされたEntityをmergeする必要もないし、SessionBeanが生きている間は、一度アクセスしたEntityはキャッシュされているので、パフォーマンスも向上します。問題点は、一度アクセスしたエンティティをキャッシュしているので、メモリを大量に消費することです。現在のJPAでは、キャッシュを全部クリアするという大雑把なメソッドしか用意されていないので困り者です。また、StatefulSessionBeanをきちんと削除してあげないとキャッシュされたEntityが解放されないと言う問題もあります。これは、Seamでは、StatefulSessionBeanのライフサイクルを自動的に管理するので、解決されているように思えますが、ユーザが最後まで処理を行わずに、途中で処理を止めた場合は、StatefulSessionBeanが削除されずに残ってしまうのではないかと思います。Seamに詳しい人、間違っていたら教えてください。
結論を書くと、今のJPAは、かなりの問題を抱えています。素で使うのはお勧めしません。JPAは、JPAのことを良く知っているフレームワークと組み合わせることをお勧めします。今の時点で、JPAを使うのに良いと思うフレームワークは、Seasar2のEasy EnterpriseファミリーとSeamです。Easy Enterpriseファミリーは、SQLの問題もView層におけるlazy loading問題も解決しているのですが、最大の問題は、S2Hibernate-JPAとKuinaDaoが正式リリースされていないこと。早くリリースしてくださーーい。
Seamは、大量にメモリを使うことが苦にならないようなシステムなら、良いのではないでしょうか。SQLの扱いが貧弱な問題点は、HibernateのSessionを直に呼ぶことで解決できるでしょう。