初めてのJPA--シンプルで使いやすい、Java EEのデータ永続化機能の基本を学ぶ

Oracle Java & Developers編集部
2015-07-08 17:00:00
  • このエントリーをはてなブックマークに追加

「永続化」の意味

 EntityManagerのメソッドで操作したエンティティは、いったんEntityManagerの永続性コンテキストに配置されます。例えば、メソッドcreateでエンティティに対する「新規登録」の操作を行っても、その時点ではデータベースへの書き込みは行われません。実際には、「このエンティティは、データベースに新規登録するデータである」というマークを付けて永続性コンテキストに配置されるだけです。これがオブジェクトの「永続化」であり、EntityManagerによってデータベースへの格納が可能になった状態であることを意味しているのです。


※クリックすると拡大画像が見られます

※クリックすると拡大画像が見られます

JSFとの親和性が高いJPA

 JPAはJava EEの一部であることから、Java EEの他のAPIと容易に組み合わせて使えるよう工夫されています。例えば、JSF(JavaServer Faces)を使用すると、JPAの扱いがとても簡単になります。

 JSFでは、WebアプリケーションのWeb層とビジネス・ロジック層の間でデータ管理を行うBacking Beanクラスを、@Namedと@RequestScopedという2つのアノテーションを付けて作ります。このクラスを使うことで、Webフォームのフィールド項目とプログラムの変数をバインドすることができます。


※クリックすると拡大画像が見られます

 また、JSFではWebページ内のボタンがクリックされた際に実行するメソッドを指定できます。そのため、例えば「登録」ボタンがクリックされたら、EJBクラスのメソッドcreateを実行するといったことが行えます。


※クリックすると拡大画像が見られます

 ちなみに、上図に示したBacking Beanのメソッドcreateでは、Webフォームから受け取ったデータを基にしてエンティティ(Employeeエンティティのempオブジェクト)を生成し、「db.create」によってデータベースに登録するという処理を行います。この「db」が、EJBクラスであるEmployeeDbのオブジェクトです。

エンティティとデータベース・テーブルのマッピング

 JPAでは、エンティティとデータベース・テーブルをどのように対応づける(マッピングする)かについて、その大枠が標準で決められています。

 例えば、「エンティティ・クラス名」は「テーブル名」にマッピングされ、エンティティ・クラスの「フィールド変数名」はテーブルの「カラム名」に対応づけられます。そして、@Idアノテーションにより、フィールド変数が主キーに変換されるといった具合です。


※クリックすると拡大画像が見られます

 マッピングをもう少しきめ細かく指定したい場合に備え、デフォルト設定を変更するためのアノテーションも用意されています。ここでは、それらについての説明は割愛しますが、どのようなアノテーションがあるのかを下表にまとめましたので参考にしてください。

【表2:デフォルトのマッピングを変更するためのアノテーション】

アノテーション名 説明
テーブルの構成を指定するアノテーション
@Table RDBのテーブル名を指定する。カラムに対する制約なども指定できる
@Secondarys
@Secondary
@Column
エンティティを複数のRDBテーブルに分割する
@Embeddable
@Embedded
複数のクラスで1つのエンティティを構成する
主キーを指定するアノテーション
@Id
@GeneratedValue
主キーの自動生成を指定する
@Embeddable
@EmbeddedId
@Embeddableを付けたクラスのオブジェクトを主キーにする。次の@IdClass、@Idを使う方法とはエンティティで主キーに指定する方法が異なるだけで実質的には同じもの。こちらの方法のほうが簡単
@IdClass
@Id
@IdClassを付けたクラスのオブジェクトを主キーにする
フィールドに個別の属性を指定するアノテーション
@Column フィールドに対応するテーブル・カラムを指定する
@Basic 遅延フェッチを指定する
@Lob ファイルなど大きいデータであることを示す
@Enumerated 列挙を、名前と序数のどちらで記録するか指定する
@TemporalType Date/Calendar型の値のデータベース型を指定する
@ElementCollection 基本型のList、Set、MapをRDBに割り付ける
@CollectionTable 割り付け先のテーブル名を指定する
@MapKeyColumn 割り付け先テーブルでのキー・カラム名を指定する
@Column 割り付け先テーブルでのカラム名を指定する
@Transient データベースに保存しないフィールドを指定する

JPA使いこなしの肝「オブジェクト/リレーショナル・マッピング」

 続いて、JPAを使いこなすうえでの肝となるオブジェクト/リレーショナル・マッピングについて説明します。JPAのオブジェクト/リレーショナル・マッピングには、次の5つのタイプがあります。

  1. One-to-One(片方向)
  2. One-to-Many(片方向)
  3. One-to-One(双方向)
  4. One-to-Many/Many-to-One (双方向)
  5. Many-to-Many(双方向)

 以下、それぞれのポイントを簡単に見ていきましょう。

(1)One-to-One(片方向)のマッピング

 「One-to-One」とは、例えば「Customer(顧客)」オブジェクトと「Information(顧客情報)」オブジェクトを「1対1」の関係で結ぶことを意味します。


※クリックすると拡大画像が見られます

※クリックすると拡大画像が見られます

 1対1関係のエンティティの定義は、通常のJavaクラスを定義するのと同じです。上図のクラスCustomerとInformationなら、それぞれ下図のように定義します。


※クリックすると拡大画像が見られます

 また、これらのエンティティを使ったプログラム側の永続化処理は、次のように定義します。


※クリックすると拡大画像が見られます

※クリックすると拡大画像が見られます

 ここでポイントとなるのは、データベース操作を実行するクラス(この例では、クラスCustomer)にカスケード処理を指定することです。カスケードの指定は次のように行います。

【リスト2:カスケードの指定】
@OneToOne(cascade = {…略…})

 例えば、カスケード指定を「cascadeType.ALL」と書けば、Customerオブジェクトに対するデータ操作(登録、更新、削除など)が、すべてInformationオブジェクトに連動するようになります。


※クリックすると拡大画像が見られます

 なお、カスケード処理には、下表に示すようにさまざまなタイプがあります。

【表3:カスケード指定のタイプ】

カスケード指定 カスケードするイベント 対応するメソッド
CascadeType.PERSIST 新規保存 persist
CascadeType.MERGE 更新 merge
CascadeType.REMOVE 削除 remove
CascadeType.REFRESH 永続性コンテキストのエンティティをデータベースから再取得した値で更新する refresh
CascadeType.DETACH 永続性コンテキストからエンティティを分離する detach
CascadeType.ALL 上記のすべての操作を指定する  

 これらをさまざまに組み合わせて、独自のカスケード指定を作ることもできます。

(2)One-to-Many(片方向)のマッピング

 「One-to-Many(1対多)」のマッピングは、例えば1つの「注文(Cart)」に複数の「注文明細(Orderline)」をひも付けるといった場合に使用します。


※クリックすると拡大画像が見られます

 このタイプのエンティティを定義する際のポイントも、One-to-Oneの場合と同様、データベース操作を実行するクラス(Cart)にカスケードを指定する点です。指定の方法は、「@OneToMany(cascade = {…略…})」と「@ManyToOne(cascade = {…略…})」のどちらでもかまいません。


※クリックすると拡大画像が見られます

(3) One-to-One(双方向)のマッピング

 One-to-Oneのマッピングは、2つのオブジェクトが互いに参照し合う双方向型にすることもできます。


※クリックすると拡大画像が見られます

 このタイプのエンティティを定義する際のポイントとしては、次の2つが挙げられます。

  • データベース操作を実行するクラスに「@OneToOne(cascade = {…略…})」を付ける
  • いずれかのクラスに「@OneToOne(mappedBy = …略…)」を付ける

 このうち、なぜmappedByの指定が必要なのかというと、双方向の参照関係にあるオブジェクトは互いに相手のオブジェクトを保持しているため、mappedByの指定がないと片方向の参照(外部キー結合の構造)が2つできてしまうからです。その無駄を避けるために、mappedByをどちらか一方のクラスに指定して、そのクラスを"関係の被所有者"とし、もう一方を"関係の所有者"とする必要があるのです。


※クリックすると拡大画像が見られます

(4)One-to-Many/Many-to-One(双方向)

 「One-to-Many/Many-to-One(双方向)」の関係を、先ほどの「Cart(注文)」と「Orderline(注文明細)」の関係を例にとって表すと、「1つ1つのOrderlineにCartの情報を持たせ、明細を見れば、どの注文かがわかるようにする」といった具合になります。


※クリックすると拡大画像が見られます

 このタイプのエンティティを定義する際のポイントもmappedByの定義をクラスに付与することとなります。ただし、「One-to-One(双方向)」の場合とは異なり、mappedByの指定は必ず「One-to-ManyのMany側」に記述します。指定形式はOne-to-One(双方向)と同じく「OneToOne(mappedBy = …略…)」となります。


※クリックすると拡大画像が見られます

(5)Many-to-Many(双方向)

 最後の「Many-to-Many(双方向)」とは、エンティティが「多対多」の関係で相互に結ばれるというものです。この関係を使うケースは少ないでしょうが、例えば「俳優(Actor)」と「映画(Movie)」を関連付ける場合などが考えられます。


※クリックすると拡大画像が見られます

 このタイプのエンティティ定義は次のように行います。


※クリックすると拡大画像が見られます

 ここでも、mappedbyを指定することがポイントとなりますが、記述するクラスは「One-to-One(双方向)」の場合と同様、どちらのクラスでもかまいません。

このサイトでは、利用状況の把握や広告配信などのために、Cookieなどを使用してアクセスデータを取得・利用しています。 これ以降ページを遷移した場合、Cookieなどの設定や使用に同意したことになります。
Cookieなどの設定や使用の詳細、オプトアウトについては詳細をご覧ください。
[ 閉じる ]