Hibernate小ネタ

現場で色々使ってるうちにわかったことがいくつか。

バージョニングを行っている永続化クラス内にある、関連する永続化クラスのコレクション(Setなど)に新しい永続化クラスを追加した場合、元の永続化クラスに対して変更をかけていなくてもUPDATEが行われる

例えば、Aクラスが1対多の関係を持つBクラスのSetを持っていた場合、そのSetに新しいBオブジェクトをaddすると、BオブジェクトがINSERTされるだけでなく、AオブジェクトもUPDATEされます。Aクラスがバージョニングを行ってない場合はUPDATEされません。つまり、オブジェクトとしては状態が変化してるので、バージョンを上げるべき・・・という考えなのかな? これを避けようと思ったら、Setに追加するのではなくSession(EntityManager)でsave(persist)してやる必要があるみたいです。

mergeした場合、@Transientなプロパティはコピーされない。

@Transientって概念自体がアノテーションありきの場合の話で、元々Hibernateマッピングしてないプロパティは無視する筈なので、よく考えればこれは当たり前の動きなんですけど・・・最初はよくわからなくてハマりました。結局、HibernateのMergeEventListenerを使ってTransientなプロパティをコピーするクラスを自作することにしました。

Timestampによるバージョニングは、デフォルトではJVMのシステム時刻を使って行われる。

DBのTIMESTAMPを使うには、プロパティの型を「dbtimestamp」に定義する必要があります。Hibernate Annotationsを使う場合は@Typeアノテーションを利用します。hbm.xmlを使う場合は・・・やったこと無いけど、多分「generated」属性を使うのかな?

Hibernate EntityManagerのデフォルト設定では、トランザクションの前にflushされ、そしてSessionはcloseされない

ただし、EntityManagerFactory.getEntityManagerを使ったときはコミット後にSessionがcloseされます(このメソッドは無くなる予定ですが)。createEntityManagerでEntityManagerを作成して自分でcloseすると、トランザクション前に自動的にflushしようとして、既にSessionがcloseされてるのでエラーになってしまいます。次にでるバージョンではこの問題に対応してるみたいです。これが出来ないと、リスナーで一時的なSession(EntityManager)が利用出来ないので困ってしまいました。今はSessionFactoryImplementorを使って、autoFlushもautoCloseもしないSessionを作成して使ってます。

永続化クラスの継承戦略を利用している場合、プロキシーに注意が必要

FETCH JOINで関連オブジェクトを同時に取得した場合は、フィールドには継承戦略に則ったオブジェクトがセットされるのですが、LAZYロードの場合はCGLIBで作成されたPROXYオブジェクトがセットされてます。なので、フィールドが親クラスの型で定義されてある場合、継承戦略通りの子クラスにキャストすることができなくなってしまいます。これって対処方法あるのだろうか?・・・関連を持つオブジェクトのgetterメソッドにアスペクトをかけて、getterメソッドを呼び出したときにLAZYロードが行われるように・・・とか出来たらいいんでしょうけど。
話は変わりますが、Hibernate3.2からはCGLIBとの選択でjavassistも使えるようになるみたいですね。速度とか結構変わってくるのだろうか?