JSR 317: Java Persistence 2.0
「JSR 317: Java Persistence 2.0」はJavaオブジェクトの永続化のためのAPIで、JSR 220: Enterprise JavaBeans 3.0(EJB 3.0)の一部として標準化されていたJava Persistence API 1.0(JPA 1.0)の後継バージョンにあたる。2.0からはEJBから完全に独立したJSRとして仕様の策定が進められており、EJBのアップデートであるEJB 3.1とともにJava EE 6で採用される予定。
※ JSR 317の名称は「Java Persistence 2.0」だが、Early Draftでは従来と同じ「Java Persistence API 2.0」になっており、またスペックリードのLinda DeMichiel氏もJavaOneにおけるセッションで「Java Persistence API 2.0」の名称を使っていた。最終的にどちらが正式名称になるのはいまいちはっきりしない。
JPA 1.0はもともとEJB 3.0のリリース直前に単体のAPIとして分割された経緯があり、オプションの機能が多く、標準化が十分だとは言えなかった。そこで2.0では新機能の追加よりも既存機能の補完やオプション機能の標準化に重点を置いており、より柔軟な永続化を実現することが目標となっている。
Java Persistence 2.0の要点についてはこの記事を参照して欲しい。現在はJSR 317のEarly Draftが公開されたほか、JavaOneにおけるセッションなどによって同APIで拡張される機能のうちいくつかの概要が明らかになったので、今回はその主たるもの紹介したい。
コレクションのサポート強化
Java Persistence 2.0では@ElementCollectionというアノテーションが追加され、これによってコレクションで指定されたフィールドの値を主テーブルに関連付けられた"コレクションテーブル"に格納することができるようになる。たとえばリスト1のようなエンティティの場合、データベース上にはPERSONとは別にPERSON_NICKNAMEというコレクションテーブルが用意され、nickNameに格納されたデータはPERSON_NICKNAMEに永続化される。この例での場合、PERSON_NICKNAMEはPERSONに関連付けるPERSON_SSNと、nickNameのデータを表すNICKNAMESという2つのカラムを持つという。
リスト1 コレクションオブジェクトの永続化
@Entity
public class Person {
@Id protected String ssn;
protected String name;
protected Date birthDate;
@ElementCollection
protected Set nickNames;
}
コレクションテーブルは@CollectionTableや@Columnなどのアノテーションによってカスタマイズできる。たとえばリスト2.2のようにすると、テーブル名はALIASES、カラム名はALIASになる。
リスト2 コレクションテーブルのカスタマイズ
@Entity
public class Person {
@Id protected String ssn;
protected String name;
protected Date birthDate;
@ElementCollection
@CollectionTable(name="ALIASES")
@Column(name="ALIAS")
protected Set nickNames;
}
Embeddableタイプについてもコレクションがサポートされ、基本型と同様に@ElementCollectionアノテーションで指定できる。コレクションテーブルのカスタマイズは、@CollectionTableでテーブル名を指定できる点は同じだが、カラム名の指定は@AttributeOverrideというアノテーションを用いてリスト3のように行う。この例では、streetフィールドの値がHOME_STREETというカラムに格納される。
リスト3 Embeddableタイプのコレクション
@Entity public class RichGuy extends Person {
@ElementCollection
@AttributeOverride(name="street", column=@Column(name="HOME_STREET"))
protected Set<Address> vacationHomes;
}
@Embeddable public class Address {
protected String street;
protected String city;
protected String state;
}
なお、Embeddableタイプについてはコレクション関連の機能以外にも、EmbeddableクラスがEmbeddableなフィールドを持つような構成のサポートや、ManyToOne/OneToOne/OneToMany/ManyToManyといったリレーションシップのサポート、@AssociationOverrideアノテーションによるリレーションシップのカスタマイズが可能になるなど、大幅な拡張が行われる予定だ。
一般化(Generalize)されたMapのサポート
Java Persistence 2.0のMapでは、Genericsを用いてキーと値のそれぞれに基本型、Embeddableタイプ、エンティティを使用することができる。また@ElementCollectionを指定することもでき、コレクションテーブルは@CollectionTableや@AttributeOverrideによってカスタマイズ可能となっている。たとえばリスト4はキーにMovieエンティティ、値にIntegerを指定している。この場合、VIDEOSTORE_VIDEOINVENTORYというコレクションテーブルが作成され、そのVIDEOINVENTORY_KEYカラムがMOVIEテーブルに関連付けられる。
リスト4
@Entity
public class VideoStore {
@Id int id;
String name;
Address location;
@ElementCollection
Map videoInventory;
}
@Entity
public class Movie {
@Id int id;
}
ソートされたリストのサポート
JPA 1.0エンティティのフィールドがListである場合、@OrderByアノテーションを指定することでデータベースから取得した結果を自動的にソートすることができる。しかしこれはあくまでも検索結果に対するソートであって、ソート結果が永続化に反映されるわけではない。
2.0では@OrderColumnというアノテーションが追加された。これを指定したフィールドは自動的にソートされ、それが永続化に反映される。@OrderColumnはone-to-manyまたはmany-to-manyなフィールドに対して使用し、@OrderByと併用することはできない。
悲観的ロックのサポート
JAP 1.0における並列処理は楽観的ロック(Optimistic lock)のみをサポートしている。2.0からは悲観的ロック(Pessimistic Lock)、すなわち更新したいリソースを照会した時点からロックを保持する機構がサポートされる。このためロックモード(LockModeType)は従来のREAD/WRITEに
- OPTIMISTIC
- OPTIMISTIC_FORCE_INCREMENT
- PESSIMISTIC
- PESSIMISTIC_FORCE_INCREMENT
- NONE
の5種類が追加されるとなる。OPTIMISTICとOPTIMISTIC_FORCE_INCREMENTは、それぞれREAD、WRITEと同じ意味だとのこと。ただし、これらの名称はまだ確定ではないそうだ。
この新しいロック機構のために、EntityManagerクラスやQueryクラスの関連するメソッドに修正が加えられるほか、@NamedQueryアノテーションにlockMode要素が追加される。また、javax.persistence.lock.timeoutによってロックの設定のためのヒントが提供されるとのことだ。
悲観的ロックの処理においてデッドロックが発生した場合にはPessimisticLockExceptionがスローされる。またタイムアウトが発生した場合にはLockTimeoutExceptionかPessimisticLockExceptionがスローされる。LockTimeoutExceptionではステートメントのロールバックが行われ、PessimisticLockExceptionではトランザクションのロールバックが行われるという。
ロードマップ
今回公開されたEarly DraftはO/Rマッピングやモデリング、ランタイムAPIなどにフォーカスされたものだった。今後はクエリーの部分にフォーカスし、次回のドラフトに組み込んでいく予定とのことである。
なお、JSR 317の参照実装にはEclipseプロジェクトによる永続化フレームワークである「EclipseLink」が採用されることが決定している。同実装はGlassFish V3とともに利用できるようになる予定だ。