Hibernate EntityManagerその7

急いで調べてるので、単にこの日記をメモ書き代わりに使ってる状態になりつつありますが・・・続きを。
Hibernate EntityManagerのリファレンスを取りあえず読み終わり、今はHibernate Annotationsのリファレンスを読んでます。一応EntityManagerの基本的な使い方はわかったので、後はAnnotationの設定方法を調べながら、各メソッドを試している状態。とりあえず気になった部分を箇条書きすると・・・

refreshメソッドを使うと、関連するオブジェクトも含めて全て情報を取得する。

Entityオブジェクトをnewで作成し、主キーとなる値をsetした後に、refreshメソッドに引数として渡してやると、Entityのカスケード設定に関係なく、関連するEntityを含めて全てのデータを一気に取得出来ます。これを使えば、遅延ロードさせたくないときに便利かな?・・・と思ったのですが、このメソッドは検索結果がゼロだった場合はEntityNotFoundExceptionを投げるんですね。だったら、予め主キーだけをQueryで検索すればいいかなと思ったんですが、QueryのgetSingleResultメソッドも、結果がゼロだった場合EntityNotFoundException(NoResultExceptionに変更になる予定らしい)を投げます。結局、例外を出さずに一気にデータを取得しようとしたら、主キーをQuery.getResultSet()でリストで取得し、そのサイズが1だった場合に初めて主キーが取得できて、それをEntityのIDカラムにセットしてrefresh・・・って、ここまでやるくらいなら、EJB-QLで一気に取得した方が早いか(汗)
また、このrefreshメソッドは、引数のEntityの中に含まれてるEntityをまず一つずつrefreshし、最後に引数のEntityを一気にrefreshしてました。階層が深いEntityを渡す時はいくつもSQLが発行される可能性があるので、使用には注意が必要そうですね・・・

Inheritance

Hibernateを勉強してたとき、この継承関連からちょっと理解できなくなって投げ出してしまってました(汗)今回は投げ出すことが出来ないので、一から勉強し直しました。
EntityBeanの継承タイプは3通り。

Table per class

クラス毎に別々のテーブルにマッピングされる。親クラスを指定して検索すると、子クラスも含めてUNIONで取得してくれる。ID自動作成してる場合、IDENTITYやAUTOは使用できない(UNIONするときにIDが被ると駄目ってことかな?)
作成されたSQL文を見てると、UNIONして使いたい、似た項目が並んだテーブルを管理するときに有効かなという気がしました。

Single table per class hierarchy

クラス階層が1つのテーブルにマッピングされる。クラスの種類を表すカラムを@DiscriminatorColumnで定義する(定義しなければ「TYPE」という名前のカラムに対して自動的に定義される)。子クラス毎に、この@DiscriminatorColumnに設定する値を@Inheritance(discriminatorValue="hoge")で定義する。
・・・つまり、1つのテーブルに入れるデータの種別によって入力するカラムが異なる場合に有効っぽいですね。親クラスを指定してリストを取得すると、中には種別によって異なる子クラスが入ってるので、リスト取得後の場合分けロジックに使えそう。
discriminatorValueの一覧を自作Enumクラスで定義出来れば便利そうなんですが、Annotationに定義できるのは「定数型」だけだそうで、enumのname()メソッドを投げてもエラーになってしまいます。JavaのAnnotationって、微妙なところで使い辛い・・・

Joined subclasses

親クラスのテーブルと子クラスのテーブルがJOINで結合する。
1対1のテーブル関係で、主キー同士で繋がってるような場合に使えそうですね。Single table per classでは1つのテーブルの中で種別毎にカラムが分かれて、それを子クラスにマッピングしてましたが、その子クラスの部分が別テーブルに出たようなイメージか・・・


3つのパターンを見てると、それぞれテーブルの関連性の主なパターンをクラスの継承関係に当てはめているんですね。ということは、テーブル定義からクラスを自動作成してEntity作成完了・・・というだけでは、EntityBeanの真の力を引き出せないので、テーブル定義をしっかり把握して、データの集合をどうクラス化するかという作業が重要・・・ってことか。つまり、それが本当のO/Rマッピングってことなのかな?

Mapping entity bean associations One-to-one

関連の定義については、外部キーと主キーで関連を定義するのはそんなに難しくなかったのですが、主キーを使わない関連性の設定の方法がよくわかってませんでした。Hibernate Annotationsのリファレンスの以下の内容を読んで、ようやく理解出来ました。

A Customer is linked to a Passport, with a foreign key column named passport_fk in the Customer table. The join column is declared with the @JoinColumn annotation which looks like the @Column annotation. It has one more parameters named referencedColumnName. This parameter declares the column in the targeted entity that will be used to the join. Note that when using referencedColumnName to a non primary key column, the associated class has to be Serializable. Also note that the referencedColumnName to a non primary key column has to be mapped to a property having a single column (other cases might not work).

@JoinColumn(name="hoge1", referencedColumnName="hoge2")みたいに設定するんですね。試しにやってみたら、findメソッドでもEJB-QLでも普通に主キー以外のキーで関連の設定が出来ました。ちなみに、OneToOneの定義に主キー以外のカラムで関連づけしていた場合、1対多のデータを入れてしまうとEntity取得時にエラーになりました。まぁ当然ですね。
ちなみに、この機能を試している時に、

<property name="hibernate.hbm2ddl.auto" value="create"/>

この機能を有効にして、Entityからテーブル定義を自動作成するようにしていたのですが、主キー以外カラムでOneToOneの関連付け定義をして動かした時に、テーブルに対してユニーク制限が追加されました。こんな細かい部分まで自動で作ってくれるのか・・・便利といえば便利だけど、テーブル側を定義の正とする場合はあまり乱用しないようにしないと、気付かないうちにテーブル定義を色々更新してしまいそうですね。