読者です 読者をやめる 読者になる 読者になる

Eclipse+JSF+JPAで作るアプリ(5)―JPAのエンティティ

前回までに、Glassfishの設定、DBの設定、web.xml、persistence.xmlと設定ばかりでしたが、今回から数回に分けてJPAの実装を行います。

さて、早速ですが仕様を変更しました。
『本』をやめて、『映画』にしました。サムネイルやレィティングなどのGUIの要素を追加しやすいからです。

通常の業務系Webアプリケーションでは、ログインする仕組みがあるでしょうから、ユーザから作り出します。しかし、

  1. ユーザのエンティティから作成しようとすると、
  2. ユーザは、映画の貸出履歴の参照を持つため、そのエンティティも作成する必要があります。貸出履歴は、映画とユーザを参照を持つので、映画も必要です。
  3. 結局、映画のエンティティも作ります。

JPAについては、詳しくは説明せず、少し駆け足で記述していきます。
詳細は、寺田佳央さんのはじめての Java Persistence API | 寺田 佳央 - Yoshio Terada がわかりやすいでしょう。

今回の範囲では、

がメインです。

JPAのエンティティの作成

JPAでのエンティティとは、ER図のEや、BCE分析でのEを指し、データベースに保存するためのオブジェクトを指します。

『ユーザ』(User)には、以下の属性を持たせます。
属性 内容
id アプリケーションで一意となるID。
account アカウント名。ユニークなキー。null禁止
password パスワード
name 名前、null禁止
e-mail メールアドレス
isAdmin 管理者か否か
lendHistories ユーザが借りた『貸出履歴』コレクション
『貸出履歴』(LendHistory)には、以下の属性を持たせます。
属性 内容
id アプリケーションで一意となるID。
lend-user 借りた『ユーザ』への参照
movie 借りられた『映画』への参照
lend-date 借りた日
due-date 返却締切日。超えても特にペナルティはないです。
return-date 返却日
review レビュー(感想)
starRating 5つ星までの評価。GUIがあります。それを使いたいための属性です。

貸出履歴は、親に、UserとMovieを持つためライフサイクルが特殊になります。ここでは、簡略化しMovieとライフサイクルを共にするよう実装します。
簡単に言えばUserを削除しても、履歴は残ります。Movieを削除すると履歴は消えます。

『映画』(Movie)には、以下の属性を持たせます。
属性 内容
id アプリケーションで一意となるID。
title タイトル、ユニークとする(厳密には違うかも)。null禁止
outline あらすじ
category 映画のカテゴリ
isLent 貸出中か否か
lendHistories ユーザが借りた『貸出履歴』コレクション。親のオブジェクトの操作は伝播させます


Eclipseには、この属性を記述し、getter/setterを自動生成させればほぼEntityクラスは完成です。

それでは実際のコードを見てみましょう。
エンティティはすべてsample.yourlibrary.entityパッケージです。

Userクラス
@Entity
@Table(name="USERTBL")
public class User {
	@Id
	@Column(name="ID")
	@GeneratedValue( strategy=GenerationType.TABLE)
	private long id;
	@Column(name="ACCOUNT", nullable=false, unique=true)
	private String account;
	@Column(name="PASSWORD")
	private String password;
	@Column(name="NAME", nullable=false)
	private String name;
	@Column(name="E_MAIL")
	private String email;
	@Column(name="IS_ADMIN")
	private boolean isAdmin = false;
	
	@OneToMany(mappedBy="lendUser")
	List<LendHistory> lendHistories;

	//setter, getter
  • @Table(name="USERTBL") テーブル名の指定です。USERというテーブルは予約語なので当然作成できません。
  • @Id @GeneratedValue( strategy=GenerationType.TABLE) @IdでJPAが勝手にIDを振ってくれます。strategyでどのように番号を振るかのロジックが変わります。SEQUENCEなどもありますが、RDBによってはサポートしていません。
  • @Column(name="ACCOUNT", nullable=false, unique=true) カラム名、NULL禁止、ユニーク制約などを設定できます。
  • @OneToMany(mappedBy="lendUser") 1対多の関係を定義します。

OneToManyは簡単です。
後ほどManyToOneが出てきますがそちらのJoinColumnと合わせて覚えてください。
多対多の定義はかなり複雑ですので機会があれば紹介します。

LendHistoryクラス
@Entity
@Table( name= "LEND_HISTORY")
public class LendHistory {
	@Id
	@GeneratedValue( strategy=GenerationType.TABLE)
	private long id;
	@Temporal(TemporalType.DATE)
	@Column(name="LEND_DATE")
	private Date lendDate;
	@Temporal(TemporalType.DATE)
	@Column(name="DUE_DATE")
	private Date dueDate;
	@Temporal(TemporalType.DATE)
	@Column(name="RETURN_DATE")
	private Date returnDate;
	@Column(name="REVIEW")
	private String review;
	@Column(name="STAR_RATING")
	private double starRating;

	@ManyToOne
	@JoinColumn(name = "MOVIE_ID", referencedColumnName = "ID")
	private Movie movie;
	@ManyToOne
	@JoinColumn(name = "LENDUSER_ID", referencedColumnName = "
ID")
	private User lendUser;

	//setter, getter
  • @ManyToOneです。OneToManyの定義でmappedByで指定した属性にマップされます。
  • @JoinColumn(name = "LENDUSER_ID", referencedColumnName = "ID") このManyToOneとJoinColumnの2つの定義で、LendHistoryクラスのlendUserという参照は、LENDUSER_IDというフィールドを相手テーブルのIDの外部キーとして使うという意味になります。
Movieクラス
@Entity
@Table( name="MOVIE" )
public class Movie {
	@Id
	@GeneratedValue( strategy=GenerationType.TABLE)
	private long id;
	@Column(name="TITLE", nullable=false)
	private String title;
	@Column(name="OUTLINE")
	private String outline;
	@Column(name="CATEGORY")
	private String category;
	@Column(name="IS_LENT")
	private boolean isLent = false;
	
	@OneToMany(mappedBy="movie", cascade=CascadeType.ALL )
	List<LendHistory> lendHistories;

	//setter, getter
  • CascadeTypeが出てきています。ALLでは次回で触れるEntityManger.persist,removeなどのすべての操作を子に伝播させます。