S2Hibernate-JPAでエンティティの自動登録 その2

目標としていた次の機能をつくりました。

  1. 指定したエンティティをS2コンテナ経由でHibernateに登録
  2. 指定したマッピングファイルをS2コンテナ経由でHibernateに登録
  3. 指定したパッケージ以下のエンティティをS2コンテナ経由でHibernateに自動登録

3番目の機能はComponentAutoRegisterを参考に(半分コピペ...)してAnnotatedClassDetectorなるものを作りました。AnnotatedClassDetectorは指定したアノテーション(たとえばEntity.classとかEmbeddable.classとか)をもつクラスのみを返します。
s2hibernate-jpaに入れましたが、hibernateにもJPAにも依存しているわけではないのでs2-tiger本体にあった方がいいのかもしれません。ただパッケージ名に迷います...、org.seasar.framework.detectorかなぁ。
 

実は今気づいてしまったんですが、前回のエントリのid:koichikさんのコメント見たら「マッピングファイルも自動登録してもらえると嬉しいなぁ」とありますね。完全に「自動」の文字を見落としてました...すみません、現状では一個一個指定しないと登録されませんです。

S2Hibernate-JPAでエンティティの自動登録

HibernateEntityManagerには自動でエンティティを見つけてくれる機能があって便利ですが、テストクラスごとに扱うエンティティを変えたい(テストAではHogeエンティティを使うがテストBではHogeエンティティを使いたくない)ときなどには、細かい制御ができなくてちょっと不便な感じがします。
こういう場合は、たぶんテストごとにPersistence Unitを作ってテストクラスでどのPersistence Unitを使うか明示すればいいのだと思いますがpersistence.xmlの記述が増えて面倒くさそうです。persistence.xmlにこのパッケージに属するエンティティ全部対象っていうような指定ができればまだいいのですけど、そういう指定はできなさそうですし。


diconに明示的にエンティティクラスを指定したりパッケージ名だけを指定して自動登録できたりすればきっと楽チンなのかなぁと思います。ということで、S2Containerからエンティティクラスを取得してHibernateに登録する仕組みを作ろうと思います。

方法は、javax.persistence.spi.PersistenceProviderの実装(S2HibernatePersistence)を独自に作って、この中でHibernateがEntityManagerFactoryを生成する前にEjb3ConfigurationにS2Contanerから読み込んだエンティティクラスを登録しちゃうというものです。そして、S2HibernatePersistenceが呼び出されるように、META-INF/services/javax.persistence.spi.PersistenceProviderファイルとpersistence.xmlファイルにS2HibernatePersistenceを使うように記述します。

こうすれば、各テストではテストクラスで必要なエンティティのみをプログラムで登録したり、テスト用のdiconで必要なエンティティのみをパッケージ指定で登録したりできるようになるはず!テストに限らず実環境でも使えると思います。(使いたくなかったらpersistence.xmlを直せばOK)

EntityPersisterたち

HibernateのEntityをDataSetに変換したいのですが、そのとき使えそうなのがEntityPersisterです。

  • org.hibernate.persister.entity.SingleTableEntityPersister
  • org.hibernate.persister.entity.JoinedTableEntityPersister
  • org.hibernate.persister.entity.UnionSubclassEntityPersister

Entityのプロパティに紐づくテーブル名やカラム名を取得するメソッドがあって最初の印象では簡単にできるかもなんて思ったんですが、完全にテーブル構造に変換するにはすんなりいかないところもありそうです。まず、publicじゃないmethodも無理やり読まないと駄目そうです。いまわかっているのはPrimaryTableとSecondaryTableの結合に使われるカラムを返すメソッドがprotectedってことです。

protected String[] getKeyColumns(int j)

これが取得できないとEntityに含まれるSecondaryTableのカラムすべてを復元できないのです。

あと、複合キーがこんな風に定義されてたりすると

@Embeddable
public class TvMagazinPk implements Serializable {
    @ManyToOne
    public Channel channel;
    @ManyToOne
    public Presenter presenter;
}

複合キーの値を取得するのが大変です。もしかしたら簡単に取得できるAPIが用意されているのかもしれないのですけど今のところわかってないです。

上記の例はHibernate Annotationのテストケースにあるコードです。JPA的にはEmbeddableなクラスに@ManyToOneはつけるのは仕様外なんですけどHibernateではOKなんですよね。

それにしてもEntityPersister、メソッド数多いです...。

Hibernate と Data Transfer Object(DTO)

Hibernate in Actionの中でもDTOはあんまりよく言われていないですね。8.1.2の「Using Hibernate in an EJB container」ではSession FacadeよりEJB Commandパターンを推していますが、Session Facade + DTOの組み合わせの方がどう見ても楽に思えるのは僕だけではないはず。