DetachedなEntity

今日はサービスメソッド(内部は Hibernate EntityManager)と、そのコールシーケンスをテストしてました。悩まされたのが、Detached Entityの存在。Javaのまじめすぎるところが、嫌いです。

FetchType.EAGERの乱用に注意

 org.hibernate.HibernateException:
  cannot simultaneously fetch multiple bags

関連(@OneToManyなど)では、FetchType.EAGERを前提に設計していたところが多いのですが、「そんなに同時にEAGERフェッチはできませんよ」というエラーがおきてしまった。※これがHibernateの仕様なのか、JPAの仕様なのか、イマイチ分かりません。
こういう関連がPersistenceUnit内に一つでもあると、EntityManagerFactoryを生成した時点でアウトです。
とりあえずデフォルト(FetchType.LAZY)に戻すと解消します。

EAGERが使えないのなら、プロパティにアクセスしまくって、強制的にLAZYフェッチしてしまえばいいので、このエラーはあまりたいした問題ではなさそう。大問題は、次です。

Detached Entity

今までの自分の考え方に、大きな間違いがあることに気づきました。Entity Managerから抜け出したEntityは、POJOではなく、Detached Entity(分離されたエンティティ)という中途半端な状態になり、自由にはアクセスできなくなるようです。

例えば、こんな感じです。UserRoleとUserが @OneToMany の場合

 //DBから登録済みのUserRoleを取得
 EntityManager em = getEntityManager();
 UserRole role = em.find( UserRole.class, "role_name" );
 em.close();
 //この時点で、roleはdetached状態に。

 //Userを新規に登録したい
 User user = new User();
 user.setName("takeda");
 user.setUserRole( role );

 //role.addUser( user );
   //この時点でException(かんべんしてよ)
   // detached entityは、
   // 関連へのアクセスができない。
 role.setUpdatedAt( new Date() ); //これはOK。

 em = getEntityManager();
 role = em.merge( role );
         //detachedから回復。
         // 変更したプロパティupdatedAtは永続化。
 role.addUser( user );  //今度はOK。
 em.persist( user );   //roleとuserの関連もろとも永続化。
 em.close();

私は、@Entityが宣言されたPOJOは、JSFからみればDTO(POJO)EntityManagerからみればEntityである。と思い込んでいました。なので、今作ってるシステムでは、JSFからEntityのプロパティを直接触って永続化する方法を取っています。

しかし、どのコンテキストから見てもあくまでもEntityだ、ということになると、JSFから触るのは確かにイヤです。Open Session In Viewなんてのも、疎結合もクソもなくなるし。

あれー、どうすればいいんだろー?と思ったら、とっくの昔に先人達のすばらしいエントリがありました。さすがは、はてな。

 koichikのひとりごと
 矢野勉のはてな日記

どちらの意見も、うんうん、そうだそうだ、と。結局は、koichikさんの結論のように、DTOで層分離しなければならないんでしょう。

Javaは真面目すぎる

それはわかるんですが、でも、もっとラクしたい。だって、POJOなんでしょ?

そもそもDetachedの仕様はヘン。というかおせっかいがすぎます。
POJOはPOJOにしてくれ、頼むから。
たぶん、EntityとDBの整合が取れなくなることを防いで、壊れる原因になるメソッドには触れないようにしてるのでしょうが・・
壊したりしないから、触らせてほしい。

それに、Detachedの状態でLAZYフェッチしたときは、nullが返ればそれで十分です。それで困る人って、いる?Exception吐く方が困るんですけど。

そのくらいは開発側が責任とります
こんなヘンな縛りがなければ、どれほどラクになるだろうか。Javaのハタ迷惑なクソまじめっぷりを感じて、ちょっとイライラしています。

・・・さてそうは言っても、仕様だからショウガナイとして、これからどうしよう。DTOを挟むか、このまま突っ走るか。・・・

問題になるのは関連操作だけだから、現状のままで、行けるところまで行こうかな。それで7割がうまくいくなら御の字か。



コメント
くりちゃん
2007/01/16
あけましておめでとうございます。
やっと正月ボケから脱しました(笑

> FetchType.EAGERの乱用に注意
 Hibernate In Actionによると、Hibernateでは1つのマッピングされた永続化クラスに対して、コレクションのイーガーフェッチはただ1つだけに限定されるということで、すくなくともHibernateでは仕様のようですよ。理由としては、複数のコレクションを単一のクエリでフェッチすることは、結果がデカルト積になってしまうからだそうです。
 おそらく複数のクエリでフェッチしないのは、トランザクション独立性の問題をHibernate内で起こしたくないということだと思われます。

> Detached Entity
 DTOでモデルの2重管理なんて嫌だなぁと思ったけど、ひがやすおさんのブログにDxoについて書いてあって、確かにプレゼンテーションモデルという考え方があって、副次的(?)に遅延フェッチの問題も解決するならDTOもいいかなぁと思い始めました。
http://d.hatena.ne.jp/higayasuo/20050817
武田ソフト
2007/01/17
くりちゃん、今年もよろしくお願いします。
Visual Webの考え方なら、プレゼンテーションモデル=ManagedBeanですよね。「JSFが推奨している形」ともいえるかもしれません。そうなると、やっぱりget/setが面倒なので、今回は採用しません。自分は思想家ではないですし、楽な方へ流れますw
ただ、あんまりメンドくさがってると、他のところで面倒なことになるような気もしますが・・・。
EAGERの件、情報ありがとうございます。実はTopLinkの方では、このエントリのようなイライラがなかったので、そっちを採用してみようかと思ってます。今度また、エントリします。
ごめんください
2007/11/02
基本的な質問ですがEAGERとLAZYってなんですか?Akelosの記事を読んで検索したところ、こちらのページに来ました。Java用語?
武田ソフト
2007/11/02
O-Rマッパ全般の用語だと思います。関連データを一気に取得する(EAGER)か、必要になったときに取得する(LAZY)かの違いです。
確かにググっても、わかりやすい説明でてこないですね。

コメントしてください

closed.