Topics
Enterprise Architecture
Spring 2.0でのJPA(Java Persistence API)の利用
Pages:
1,
2,
3,
4,
5,
6
さてここまで、クエリや更新を処理するデータアクセスコードの例を見てきましたが、永続クラス自体はどうなのでしょうか。一体、どのようなものなの でしょうか。POJOにメタデータを加えたものだとお考えください。ただし、永続POJOは、数々のルールやデザインパターンに従う必要があります。これ らのルールには、JPAで規定されているものもあれば、以下に述べるように、アプリケーションアーキテクチャ全体から結果的にもたらされたものもありま す。
まず、メタデータに関する注意事項を述べておきます。JPAでは、永続性とオブジェクト/リレーショナルマッピングに関係するJPAメタデータを外 部XMLファイル、Java 5.0注釈、あるいはその両者の組み合わせのどれで指定するかを開発者が選択することができます。医療記録管理アプリケーションでは、JPAメタデータの 指定にJava注釈を使用します。この方式には、メタデータがその適用対象のJavaクラス、フィールド、メソッドと一緒に記述されるので、理解しやすい という利点があります。メタデータをコードとは別にしておきたいという場合には、注釈ではなくXMLを使用することができます。
以下に示す
Patientクラスとそのフィールド宣言に目を通して、注釈付きのJPAクラスとはどのようなものなのかを調べてみましょう。
package com.bea.medrec.domain;
import javax.persistence.*;
import kodo.persistence.jdbc.ElementJoinColumn;
@Entity()
public class Patient implements Serializable
{
@Id()
@GeneratedValue(strategy=IDENTITY)
private Integer id;
private Date dob;
@Column(name = "first_name")
private String firstName;
private String gender;
@Column(name = "last_name")
private String lastName;
@Column(name = "middle_name")
private String middleName;
private String phone;
private String ssn;
@ManyToOne(cascade={PERSIST, MERGE})
@JoinColumn (name="address_id")
private Address address;
@OneToOne(cascade={PERSIST, MERGE})
@JoinColumn (name="email")
private User user;
@OneToMany(targetEntity = Prescription.class)
@ElementJoinColumn(name="pat_id")
private Set prescriptions=null;
@OneToMany (targetEntity=Record.class)
@ElementJoinColumn(name="pat_id")
private Set records =null;
...
}
Patientクラスに
Entity注釈が付いていることに注意してください。これは、注釈を使用するすべての永続クラスに必要な指定です。
Entity注 釈が付いていれば、永続化エンジンは、このクラスがJPAエンティティであることがわかります。エンティティクラスは一般にpublicである必要はあり ませんが、MedRecの永続クラスは、Web層とWebサービス層で使用できるように、publicになっている必要があります。
エンティティクラスはまた、
Serializableを実装する必要はありませんが、
Patientク ラスは、MedRec StrutsアクションによってHTTPセッション内に設定されるので、シリアライズ可能でなければなりません。これは、セッション状態がクラスタ内の ノード間でシリアライズされる可能性のあるクラスタ環境でアプリケーションが動作できるようにするためです。エンティティクラスに関する要件としては、そ の他に、トップレベルクラスでなければならないこと、finalであってはならないこと、などがあります。要件の全リストについては、JPA仕様 (JSR220)を参照してください。
JPA実装では、エンティティクラスの状態(永続フィールド)を実行時に読み書きする必要があります。JPA仕様では、実装でエンティティのフィー ルドに直接アクセスするか、あるいはJavaBeanスタイルのアクセサメソッド(ゲッター/セッター)を呼び出すことで、それを行えるようになっていま す。使用されるアクセスの方式は、永続性注釈がどこに記述されるかで決まります。たとえば、それらの注釈がクラスのフィールドに記述される場合は、フィー ルドアクセスが用いられます。
JPAではなぜ、アクセス方法としてフィールド方式とメソッド方式の両方をサポートしているのでしょうか。答えは柔軟性です。一部には、 publicアクセサメソッドでフィールド値の検証を行うクラスがあります。これは、データベース内の値が範囲外であるために例外が送出された場合、 JPA実装にとって問題となるおそれがあります。セッターで値の検証を行うクラスの場合は、フィールドに注釈を付けるのがおそらく一番よいでしょう。一 方、仮想的な永続プロパティのようなものをサポートしたい場合は、アクセサメソッドに注釈を付けます。
JPAエンティティでは、Transient注釈(JPAで定義されている注釈)が付いていない非一時フィールドはすべて永続的です。「永続的」とは、そのフィールドがデータベース内のカラムにマッピングされるということです。なお、
Patientク ラスの永続フィールドには、注釈が付いていないものがあることに注意してください。これは、JPAで定義されるデフォルト(つまり、デフォルトカラム名) がこれらのフィールドの場合には正しかったためです。フィールド名とそのマッピング先のデータベースカラム名が異なる場合には、
Column注釈を使用して、正しいデータベースカラム名を指定する必要があります。
どのエンティティにも主キーが必要です。JPAでは、単一(シングルフィールド)主キーと複合(マルチフィールド)主キーを両方ともサポートしてい ます。実践的には、可能なかぎり単一主キーを使用し、主キー値をデータベースに自動生成させるのが一番よいでしょう。これは、JPAではオブジェクトが いったん永続化されたら主キー値を変更できないからです。このため、
Patientクラスも含め、この医療記録管理アプリケーションにおける永続クラスの大半では、自動生成された単一主キーを使用しています。複合主キーを持つクラスの例については、
Groupクラスを参照してください。
Id注釈が付いている場合は、それが主キーとなるフィールド、すなわちIDフィールドであることを示します。
GeneratedValue注釈は、新しいPatientオブジェクトが挿入されるときに主キー値がデータベースによって生成されることを示します。データベースではさまざまな方法で主キー値を生成でき、strategy属性はそのうちの1つを選択するのに使用できます。
PatientクラスではID戦略を使用していますが、これは、
idフィールドがデータベースの自動インクリメントカラムすなわちIDカラムにマッピングされること意味します。
ID戦略を用いる際には、当該クラスのequalsメソッドとhashCodeメソッドに与える影響という微妙な問題を頭に入れておくべきです。た とえば、equalsメソッドでidフィールドの値を比較する場合は、データベースへの挿入が行われる前(通常はトランザクションのコミット前)に結果的 にequalsメソッドが呼び出されることになるようなオブジェクトの使い方を絶対にしないようにする必要があります。それまでは主キー値が割り当てられ ないからです。この問題を避ける方法の1つは、クラスに自然キーになるフィールドがある場合には、生成されたキーではなくその自然キーをequalsメ ソッドで使用することです。たとえば、患者の社会保障番号などは、Patientクラスのequalsメソッドで使用するのに適した自然キーになるでしょ う。
|
|