2011年7月21日木曜日

Hibernateのオブジェクトとセッションの微妙な関係

Hibernateでデータベースを管理していると、いまでのSQLによるプログラミングと微妙な違いがあって戸惑ってしまいます。


JavaとSQLでゴリゴリやっていた当時は、Java側のオブジェクトにはデータベースのレコードのコピーがあって、メソッドでSQLを実行させて、データベースとのやり取りを行っていたので、データベースの更新についてプログラム側で完全に制御することが出来ました。


ところがHibernateを利用すると、追加、削除についてはプログラム側で制御可能ですが、更新についてはプログラム側での制御が難しいようです。
その原因は、Hibernateの一時オブジェクト、永続化オブジェクト、分離オブジェクトの3種類のオブジェクトがあるからのようです。


一時オブジェクト:Javaでnewしたオブジェクト

永続化オブジェクト:HibernateのSessionで管理されているオブジェクト(データベースのレコードと関連している)

分離オブジェクト:データベースのレコードの内容を持っているけれどもHibernateのSessionで管理されていないオブジェクト


特にプログラム側で意識していないと永続化オブジェクトがごろごろ出来あがって、ついには

org.hibernate.NonUniqueObjectException?: a different object with the same identifier value was already associated with the session

という例外を起こしてしまいます。

また、永続化オブジェクトのフィールドを変更するとHibernateが適当なタイミングでデータベースをupdateしてくれます。これは、従来のJavaとSQLになれたプログラマには???なことになってしまいます。意識しないうちデータベースのレコードが更新されているので、ちょっとおかしなことになってしまう場合もあります。


今回、開発途中で発生した問題は、上記2点の問題で振り回されてしまいました。

1番目の問題については、更新前にキャッシュをクリアしてから、更新対象となるデータを再読み込みすれば良いのでそれ程問題ではありませんでした。

問題だったのはHibernateの自動更新機能で、今回はプログラムの作成上、永続化オブジェクトのフィールドを変更した後に、特定の条件によってはその更新を行わない必要が出たのです。フィールドを変更する時に条件を判定して変更しないようにすれば問題は解決するのですが、データの編集を行うクラスと、実際に更新するクラスが異なるので、フィールド編集時には変更が必要かは判定できない仕様となっています。

いろいろと試行錯誤を行った結果以下の手法をとることにしました。

フィールド編集クラスで分離オブジェクトにして(evictメソッド参照)HibernateのSession管理からはずすことで、自動更新を回避します。

更新側では、更新の必要があった場合は、渡されたオブジェクトをupdateすることで永続化オブジェクトにして更新を行います。


Hibernateの管理ははずれるのでHibernateのメリットが犠牲になるような気もしますが、この方法が一番すっきりとできたようです。

0 件のコメント:

コメントを投稿