HibernateのSession#update(Object)

会社で他人のコードを見ていて良く見る勘違いだが、Session#update(Object)というのはそうそう使うメソッドじゃない。Hibernateのsave/updateはそのままSQLのinsert/updateに対応するわけじゃないのだ。

EJBと同じで、ここで「オブジェクト」という言葉を拡張してやる必要がある。「オブジェクト」はJVM上のObjectクラスのインスタンスとしては存在していないかもしれない。RDBのレコードとして存在している状態も含めて「オブジェクト」と考える。

POJOをnewした時点では、これは一時的な「オブジェクト」である。そのインスタンスへの参照がなくなったらもうそれを取り戻すことは(普通は)できないし、いつかはGCで本当に消滅してしまう。この一時的な「オブジェクト」を永続的な「オブジェクト」に変えるのがHibernateのSession#save(Object)だ。saveされた「オブジェクト」は永続的な存在に変わるので、インスタンスへの参照が消滅しても適切なQueryをすればSessionを通じて取り戻すことができるのだ。

この逆の操作をするのがSession#delete(Object)だといったら分かりやすいだろうか。このメソッドは永続的な「オブジェクト」を引数に取って、それを一時的な「オブジェクト」に変える。JVMの中だけを見ているなら、別にインスタンスが削除されるわけではない。

じゃあ、永続的な「オブジェクト」に変更を加えたらどうなるか。この「オブジェクト」に対応するJVM上のObjectクラスのインスタンスは参照がなくなった時点でアクセス不能になってしまう。加えた変更はどこか手の届かないところへいってしまうのだろうか? もちろん、この「オブジェクト」は永続的な存在なのだから、変更が失われるなんてことはない。トランザクションのコミット時など、適切な時点で勝手にUPDATE文が発行されてちゃんと変更が保存されるのだ。別にわざわざSession#update(Object)なんかしてやる必要はない。

この「自動的にUPDATE」っていう性質をPOJOに付け加えるためにHibernateはcglibを使っているのだな。Javaのnewではなくて、HibernateのQueryを経由して取得したインスタンスは、見た目は普通のPOJOに見えるけれども、実はHibernateがcglibで勝手に改造したクラスのインスタンスで、適切なところで勝手にSQLを発行して自分自身の状態をRDBに保存する機能が付け加わっているのだ(だからimmutableなときにはmutable="false"って指示するとパフォーマンスが上がるんだな)。

以上のことは一応全部ドキュメントに書いてあるけれども、どうもうちの会社の人たちにはあんまり通じてないみたいだ。まぁ、正確に言えばSession自体のキャッシュ機能とかがあるから、「SQLを発行」っていうのは厳密には正しくないかもしれないけれども、とにかく、「単なるJVM内のビットの塊」と「永続的なオブジェクト」の間を行き来するのかsave/deleteで、updateはそうそう使うものじゃないっていうことだけご理解いただきたい。

で、ここでassignedなidとかが絡まってくるとまた少々ややこしいのだけれど、それは今回はパスしよう。そもそもHibernateは意味無し主キーを推奨しているから、古いRDB上にJavaシステムを構築しようとしてるのでない限り、通常assignedなidは使うべきじゃないのをお忘れなく。ビジネス上の意味論はオブジェクトのアイデンティティよりは変更可能性が高いっていう発想だなー。多分。