More Related Content Similar to Beginning Java EE 6 勉強会(2) #bje_study (20) More from Kinji Akemine (12) Beginning Java EE 6 勉強会(2) #bje_study2. 目次
3. オブジェクト・リレーショナル・マッピング
4. 永続オブジェクトの管理
5. コールバックとリスナ
2012/4/24 Beginning JavaEE6 勉強会(2) 2
4. 第3章
オブジェクト・リレーショナル・
マッピング
2012/4/24 Beginning JavaEE6 勉強会(2) 4
5. 3. オブジェクト・リレーショナル・マッピング
• なぜORM?
– ObjectとRDBはパラダイムが違う
• リレーション
– Object:参照、ポインタなどで表現
– RDB:外部キーで表現
• 継承
– RDBにはない概念
…etc
– このへんの差をうまく吸収してあげる仕組みをORM
が提供する
2012/4/24 Beginning JavaEE6 勉強会(2) 5
6. 3.1 エンティティのマッピング方法
• もっとも単純なマッピング
JPA関係を
まるっとインポート
@Entityアノテーションで
永続クラスとして宣言 import javax.persistence.*;
@Entity
public class Book {
@Id
private Long id;
@Idアノテーションで private String title;
一意識別子(=主キー) private Float price;
としてidを宣言 private String description;
private String isbn;
private Integer nbOfPage;
private Boolean illustrations;
}
2012/4/24 Beginning JavaEE6 勉強会(2) 6
7. 3.1.1 Configuration-by-Exception
• 設定より規約
– クラス名→テーブル名
• Bookクラス→BOOKテーブル
• 名前を変えたい場合は@Tableアノテーションを利用
– 属性名→カラム名
• id属性→IDカラム
• 名前を変えたい場合は@Columnアノテーションを利用
– 型マッピング規則は、基本規則は決まっているが、
細かい部分はJDBCやRDBMSによって違う
– 基盤のデータベース情報はpersistence.xmlに記述
2012/4/24 Beginning JavaEE6 勉強会(2) 7
8. 3.2.1 テーブル(1) @Table
• マッピング先のテーブルを規約から外したい場
合に利用
「T_BOOK」テーブルにマッピング
※規約では「BOOK」テーブル
@Entity
@Table(name = "t_book")
public class Book {
@Id
private Long id;
private String title;
private Float price;
private String description;
private String isbn;
private Integer nbOfPage;
private Boolean illustrations;
}
2012/4/24 Beginning JavaEE6 勉強会(2) 8
9. 3.2.1 テーブル(2) @SecondaryTable
• 1つのエンティティの属性を2つ以上のテーブル
にマッピング
2つ以上の二次テーブルを使う場合は
@Entity @SecondaryTablesアノテーション
@SecondaryTables({
@SecondaryTable(name = "city"),
@SecondaryTable(name = "country")
})
public class Address {
@Id
二次テーブルとして「CITY」
private Long id; 「COUNTRY」を利用
private String street1;
private String street2;
@Column(table = "city")
private String city;
@Column(table = "city") STATEカラムはCITYテーブルに作成
private String state;
※規約ではADDRESSテーブル
@Column(table = "city")
private String zipcode;
@Column(table = "country")
private String country;
}
2012/4/24 Beginning JavaEE6 勉強会(2) 9
10. 3.2.1 テーブル(2) @SecondaryTable
• マッピングのされ方
ADDRESS
+#ID bigint
主キーが共通 STREET1 varchar(255)
STREET2 varchar(255)
<<エンティティ>> CITY
Address +#ID bigint
-id:Long CITY varchar(255)
-street1:String STATE varchar(255)
-street2:String ZIPCODE varchar(255)
-city:String
-state:String
COUNTRY
-zipcode:String
-country:String +#ID bigint
COUNTRY varchar(255)
2012/4/24 Beginning JavaEE6 勉強会(2) 10
11. 3.2.2 主キー(1) @Id, @GeneratedValue
• 主キーとしたい属性には@Idアノテーション
• 主キーの値の生成方法を@GeneratedValueアノ
テーションで指定
@GeneratedValue 振る舞い
(strategy =
***)
GenerationType.SEQUENCE シーケンス番号を利用する
GenerationType.IDENTITY ID列の値を利用する
GenerationType.TABLE 主キー用のテーブルを作成して利用する
GenerationType.AUTO RDBMSに最適の方法でよろしくする
普通はこれでOK
2012/4/24 Beginning JavaEE6 勉強会(2) 11
12. 3.2.2 主キー(2) 複合主キー
• 2つの実現方法
複合主キークラスに
複合主キークラスには
@Embeddableアノテーション
特別な宣言はなし
@Embeddable
public class NewsId { public class NewsId {
private String title; private String title;
private String language; private String language;
} }
複合主キークラスの属性に
@EmbeddedIdアノテーション @Entity
@Entity @IdClass(NewsId.class)
public class News { public class News {
@EmbeddedId @Id
private NewsId id; private String title;
@Id
private String language;
private String content; private String content;
} }
複合主キークラスの属性を再度
特別な理由がなければこっち @Idアノテーションとともに宣言
2012/4/24 Beginning JavaEE6 勉強会(2) 12
13. 3.2.3 属性(1) @Basic
• 属性とカラムのマッピング方法の基本的なオプ
ション設定を行う
– fetch = FetchType.LAZY
• サイズが大きい場合などに利用し、遅延取得する
• 属性のgetterメソッドが呼ばれたときに取得
– optional = false
• persist時などに、属性がnullの場合にはじく
2012/4/24 Beginning JavaEE6 勉強会(2) 13
14. 3.2.3 属性(2) @Column
• 属性のマッピング先のカラムに関する様々な設
定を行う
@Entity
public class Book16 { 本のタイトルは必須項目、
@Id
@GeneratedValue かつ、更新されることはない
private Long id;
@Column(name = "book_title", nullable = false, updatable = false)
private String title;
private Float price; 本の説明文は2000文字
@Column(length = 2000) ※規約では255文字
private String description;
private String isbn;
@Column(name = "nb_of_page", nullable = false)
private Integer nbOfPage;
private Boolean illustrations;
} 本のページ数は必須項目
@Basic(optional = false)と違い、
@Column(nullable = false)はスキーマ作成時のみ有効
2012/4/24 Beginning JavaEE6 勉強会(2) 14
15. 3.2.3 属性(3) @Temporal, @Transient
• Date型属性のマッピング
@Temporal(***) 振る舞い
TemporalType.DATE 年月日のみ扱う
TemporalType.TIME 時分秒のみ扱う
TemporalType.TIMESTAMP 年月日と時分秒の両方を扱う
• 永続化したくない属性
– @Transientアノテーションを付加することで永続化
対象から除外
2012/4/24 Beginning JavaEE6 勉強会(2) 15
16. 3.2.4 アクセスタイプ
• 属性アノテーションはgetterメソッドに対して
も宣言可能
属性に付加されたアノテーションを
@Entity もとにラッピング
@Access(AccessType.FIELD)
public class Customer {
@Column(length = 15)
private String phoneNumber; @Accessアノテーションが
private String email; 上書きされているので
@Access(AccessType.PROPERTY) 555文字で定義
@Column(length = 555)
public String getPhoneNumber() {
return phoneNumber;
}
@Column(length = 555) 上書きされていないので
public String getEmail() { 255文字で定義
return email;
}
}
2012/4/24 Beginning JavaEE6 勉強会(2) 16
17. 3.2.5 基本型のコレクション
• リレーションを意識せずマッピング可能
@Entity
public class Book { tagsを「TAG」テーブルにマッピング
※規約では「BOOK_TAGS」
@Id
private Long id;
@ElementCollection(fetch = FetchType.LAZY)
@CollectionTable(name = "Tag")
@Column(name = "Value")
private List<String> tags = new ArrayList<String>();
}
tagsの要素を「VALUE」カラムにマッピング
※規約では「TAGS」
BOOK TAG
+ID bigint #BOOK_ID bigint
VALUE varchar(255)
2012/4/24 Beginning JavaEE6 勉強会(2) 17
18. 3.2.6 基本型のマッピング
• Map構造も用意にマッピング可能
@Entity
public class CD { key要素を「POSITION」カラムにマッピング
※規約では「CD_TRACKS_KEY」
@Id
private Long id;
@ElementCollection
@MapKeyColumn(name = "position")
private Map<Integer, String> tracks =
new HashMap<Integer, String>();
}
CD CD_TRACKS
+ID bigint #CD_ID bigint
POSITION integer
TRACKS varchar(255)
2012/4/24 Beginning JavaEE6 勉強会(2) 18
20. 3.4 組み込み可能オブジェクト
• コンポジション構造を1つのテーブルにマッピ
ング
複合主キーで使ったやつ
@Embeddable @Entity
public class Address { public class Customer {
private String street1; @Id
private String street2; private Long id;
} @Embedded
private Address address;
}
CUSTOMER
+#ID bigint
STREET1 varchar(255)
STREET2 varchar(255)
2012/4/24 Beginning JavaEE6 勉強会(2) 20
22. 3.5.1 RDBにおけるリレーションシップ
• カラム結合
主 名 外部 主 都市
キー 前 キー キー
1 鈴木 11 11 千葉!
2 佐藤 12 12 滋賀!
3 田中 13 13 佐賀!
• テーブル結合 パフォーマンスを考えてなるべくこっち
主 名 主 都市
キー 前 キー
1 鈴木 顧 住 11 千葉!
客 所
2 佐藤 12 滋賀!
1 11
3 田中 13 佐賀!
2 12
3 13
2012/4/24 Beginning JavaEE6 勉強会(2) 22
23. 3.5.2 エンティティのリレーションシップ(1)
• カーディナリティと方向の組み合わせ
カーディナリティ 方向
以下の3種類 以下の2種類
1 対 1 1 対 1 一方向 一方向
1 対 多 1 対 1 双方向 双方向
多 対 多 1 対 多 一方向
1 対 多 / 多 対 双方向
1
多 対 1 一方向
多 対 多 一方向
多 対 多 双方向
一般的に用いられる、赤枠の3つのパターンについて
マッピングの仕組みを詳細に説明
2012/4/24 Beginning JavaEE6 勉強会(2) 23
24. 3.5.2 エンティティのリレーションシップ(2)
@OneToOne、一方向
@Entity
public class Address {
@Id
private String street1;
private String street2; 外部キーのカラム名は「ADD_FK」
} ※規約では「ADDRESS_ID」
@Entity
public class Customer {
@Id
private Long id;
@OneToOne
@JoinColumn(name = "add_fk", nullable = false)
private Address address;
}
CUSTOMER ADDRESS
+ID bigint +ID bigint
+#ADD_FK bigint STREET1 varchar(255)
STREET2 varchar(255)
2012/4/24 Beginning JavaEE6 勉強会(2) 24
25. 3.5.2 エンティティのリレーションシップ(3)
@OneToMany、一方向
• デフォルトではテーブル結合
@Entity
結合テーブル名は「JND_ORD_LINE」 public class OrderLine {
※規約では「ORDER_ORDERLINE」 @Id
@Entity private Long id;
public class Order { private String item;
@Id private Integer quantity;
所有側の外部キーは「ORDER_FK」
}
private Long id; ※規約では「ORDER_ID」
@OneToMany
@JoinTable(name = "jnd_ord_line",
joinColumns = @JoinColumn(name = "order_fk"),
inverseJoinColumns = @JoinColumn(name = "line_fk"))
private List<OrderLine> orderLines;
}
被所有側の外部キーは「LINE_FK」
※規約では「ORDERLINE_ID」
ORDER ORDERLINE
+ID bigint JND_ORD_LINE +ID bigint
+#ORDER_FK bigint ITEM varchar(255)
+#LINE_FK bigint QUANTITY integer
2012/4/24 Beginning JavaEE6 勉強会(2) 25
26. 3.5.2 エンティティのリレーションシップ(3)
@OneToMany、一方向
• カラム結合に変更可能 こっちは変更なし
@Entity @Entity
public class Order { public class OrderLine {
@Id @Id
private Long id; private Long id;
@OneToMany private String item;
@JoinColumn(name = "order_fk") private Integer quantity;
private List<OrderLine> orderLines; }
}
外部キーは所有側に宣言し、キー名は「ORDER_FK」
※規約では「ORDER_ID」
ORDER ORDERLINE
+ID bigint +ID bigint
ITEM varchar(255)
QUANTITY integer
#ORDER_FK bigint
2012/4/24 Beginning JavaEE6 勉強会(2) 26
27. 3.5.2 エンティティのリレーションシップ(4)
@ManyToMany、双方向
@Entity 指定しないと「CD_ARTIST」
public class CD { もできてしまう
結合テーブル名は「JND_ART_CD」 @Id
※規約では「ARTIST_CD」
private Long id;
@ManyToMany(mappedBy = "appearsOnCDs")
@Entity private List<Artist> artists;
public class Artist { }
@Id 所有側の外部キーは「ARTIST_FK」
private Long id; ※規約では「ARTIST_ID」
@ManyToMany
@JoinTable(name = "jnd_art_cd",
joinColumns = @JoinColumn(name = "artist_fk"),
inverseJoinColumns = @JoinColumn(name = "cd_fk"))
private List<CD> = appearsOnCDs;
}
被所有側の外部キーは「CD_FK」
※規約では「CD_ID」
ARTIST CD
+ID bigint JND_ART_CD +ID bigint
+#ARTIST_FK bigint
+#CD_FK bigint
2012/4/24 Beginning JavaEE6 勉強会(2) 27
29. 3.5.4 リレーションシップの順序(1) @OrderBy
• 属性名に対してASCまたはDESCで指定
@Entity
public class News {
@Id
private Long id;
private String content;
@OneToMany(fetch = FetchType.EAGER)
@OrderBy("postedDate DESC, content ASC")
private List<Comment> comments;
}
カラム名ではなく属性名で指定
@Entity
public class Comment {
@Id
private Long id;
private String content;
@Column(name = "posted_date")
@Temporal(TemporalType.TIMESTAMP)
private Date postedDate;
}
2012/4/24 Beginning JavaEE6 勉強会(2) 29
30. 3.5.4 リレーションシップの順序(2) @OrderColumn
• 索引カラムを追加することで順序を維持
@Entity
public class Comment {
@Id
private Long id;
private String content;
}
@Entity
public class News {
@Id
private Long id;
private String content;
@OneToMany(fetch = FetchType.EAGER)
@OrderColumn(name = "posted_index")
private List<Comment> comments;
}
結合テーブルに索引用カラム
「POSTED_INDEX」を追加
※規約では「COMMENTS_ORDER」
2012/4/24 Beginning JavaEE6 勉強会(2) 30
31. 3.6 継承のマッピング
• 3つのマッピング方式
– クラス階層ごとに1つのテーブル
• 全部まとめて1つのテーブル
• デフォルトはこの方式
– 結合サブクラス
• クラス間の差分の属性のみを別テーブル
– 具象クラスごとのテーブル
• 継承する属性も含め、クラスごとにテーブル
• オプション機能なのでディストリビューションによっては
実装されていない可能性あり?
2012/4/24 Beginning JavaEE6 勉強会(2) 31
32. 3.6.1 継承方式(1) 例題
• 以下の例題をもとにマッピング方法を説明
<<エンティティ>>
Item
-id : Long
-title : String
<<エンティティ>> <<エンティティ>>
Book CD
-isbn : String -musicCompany : String
2012/4/24 Beginning JavaEE6 勉強会(2) 32
33. 3.6.1 継承方式(2)
クラス階層ごとに1つのテーブル
• 全部まとめて1つのテーブル
省略可能 どのクラスのインスタンスかを
@Entity
@Inheritance(strategy =
識別するためのカラム「CT」
InheritanceType.SINGLE_TABLE)
※規約では「DTYPE」
@DiscriminatorColumn(name = "ct",
discriminationType = DiscriminatorType.CHAR)
@DiscriminatorValue("I")
public class Item { @Entity
@Id @DiscriminatorValue("B")
protected Long id; public class Book extends Item {
protected String title; private String isbn;
} }
Itemクラスの識別は「I」
※他のクラスも同様 @Entity
※規約ではクラス名の「Item」 @DiscriminatorValue("C")
public class CD extends Item {
private String musicCompany;
}
ID CT TITLE ISBN MUSICCOMPANY
1 I 1_t
2 B 2_t 2_I
3 C 3_t 3_M
2012/4/24 Beginning JavaEE6 勉強会(2) 33
34. 3.6.1 継承方式(3)
結合サブクラス
• クラス間の差分の属性のみを別テーブル
@Entity
@Inheritance(strategy = InheritanceType.JOINED)
public class Item {
@Id
protected Long id; 結合サブクラス方式でマッピング
protected String title;
}
@Entity @Entity
public class Book extends Item { public class CD extends Item {
private String isbn; private String musicCompany;
} }
BOOK ITEM CD
+#ID bigin +ID bigin +#ID bigin
ISBN t DTYPE t MUSICCOMPANY t
Strin TITLE Strin Strin
g g g
Strin
2012/4/24 Beginning g
JavaEE6 勉強会(2) 34
35. 3.6.1 継承方式(4)
具象クラスごとのテーブル
• 継承する属性も含め、クラスごとにテーブル
@Entity
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public class Item {
@Id
protected Long id; 具象クラスごとの方式でマッピング
protected String title;
}
@Entity @Entity
public class Book extends Item { public class CD extends Item {
private String isbn; private String musicCompany;
} }
3クラス通してIDが一意となっている
BOOK ITEM CD
+ID bigin +ID bigin +#ID bigin
TITLE t TITLE t TITLE t
ISBN Strin Strin MUSICCOMPANY Strin
g g g
2012/4/24 Strin Beginning JavaEE6 勉強会(2) Strin
35
36. 3.6.2 継承階層に含まれるクラスの種類(1)
• 抽象エンティティ
– 特に気にせず大丈夫
• 非エンティティ
– 特に気にせず大丈夫
– ただし(当たり前だが)非エンティティの属性は
マッピング対象にはならない
• マップドスーパークラス
– 非エンティティではあるが、継承先のクラスにマッ
ピング情報を引き継ぎたい場合に利用
2012/4/24 Beginning JavaEE6 勉強会(2) 36
37. 3.6.2 継承階層に含まれるクラスの種類(2)
マップドスーパークラス
• 継承先のクラスにマッピング情報を引き継ぎた
い場合に利用
@MappedSuperclass
@Inheritance(strategy = InheritanceType.JOINED)
public class Item {
@Id
protected Long id; @Entity
protected String title; public class Book extends Item {
} private String isbn;
}
IDやTITLEカラムが作成される
BOOK
+ID bigin
ただしITEMは作成されない
TITLE t
ISBN Strin
g
Strin
2012/4/24
g Beginning JavaEE6 勉強会(2) 37
38. 第4章
永続オブジェクトの管理
2012/4/24 Beginning JavaEE6 勉強会(2) 38
40. 4.2 エンティティ・マネージャ
• エンティティ・マネージャの取得
– JavaSE環境
• 作成からクローズまで実装する必要あり
– JavaEE環境
• アノテーション等を用いて簡単に取得可能
• 永続性コンテキスト
– EntityManagerで管理しているインスタンスの集合
• Manager内に同じIDのものは存在しない
– Managerは同時に複数存在できるので、違うManager間
で同じIDのインスタンスは存在しえる
• データベースへflushされるまでの一時キャッシュ
2012/4/24 Beginning JavaEE6 勉強会(2) 40
41. 4.2.3 エンティティの操作(1)
customer cust = new Customer() カーベジコレクション完了
データベースから削除されても
メモリ上には存在
メモリ内に存在
customer cust = em.find()
em.persist(cust)
em.clear()
em.detach(cust) em.remove(cust)
分離状態 管理状態 削除済み状態
em.merge(cust)
em.refresh(cust)
セッターによる更新
データベース
2012/4/24 Beginning JavaEE6 勉強会(2) 41
42. 4.2.3 エンティティの操作(2)
• 以下の例について考える
CUSTOMER ADDRESS
+ID bigint +ID bigint
+#ADD_FK bigint STREET1 varchar(255)
STREET2 varchar(255)
• エンティティの永続化
Customer customer = new Customer();
Address address = new Address("1", "2");
customer.setAddress(address);
tx.begin();
em.persist(customer); EntityManagerはキャッ
em.persist(address); シュとして動作するため、
tx.commit(); 逆でも問題なく動作する
2012/4/24 Beginning JavaEE6 勉強会(2) 42
43. 4.2.3 エンティティの操作(3)
• 2つの方法によるIDによるエンティティの検索
– find()
Customer customer = em.find(Customer.class, 1L);
if (customer != null) {
// 処理
} なかった場合はNULL
– getReference()
IDのみセットされたproxyオブジェクト
try {
Customer customer = em.getReference(Customer.class, 1L);
// 処理
} catch(EntityNotFoundException e) {
// 例外処理
}
なかった場合は例外
2012/4/24 Beginning JavaEE6 勉強会(2) 43
44. 4.2.3 エンティティの操作(4)
• エンティティの削除
Customer customer = new Customer();
Address address = new Address("1", "2");
customer.setAddress(address);
tx.begin();
addressはDBに残ったまま
em.persist(customer);
em.persist(address);
tx.commit();
tx.begin();
em.remove(customer);
tx.commit(); GCされるまでcustomerは存在
assertNotNull(customer);
2012/4/24 Beginning JavaEE6 勉強会(2) 44
45. 4.2.3 エンティティの操作(5)
• 孤立したオブジェクトの削除
– 先ほどの例では参照されない住所が取り残される
@Entity
public class Customer { 誰からも参照されない場合は一緒に削除
@Id
private Long id;
@OneToOne(fetch = FetchType.LAZY, orphanRemoval = true)
@JoinColumn(name = "address_fk", nullable = false)
private Address address;
}
2012/4/24 Beginning JavaEE6 勉強会(2) 45
46. 4.2.3 エンティティの操作(6)
• データベースとの同期
– commit()時は確実に同期される
– 明示的に同期させたい場合はflush()
tx.begin();
em.persist(customer1);
em.persist(address1);
flush()済みのデータも削除される
em.flush();
em.persist(customer2);
em.flush(); まだaddress2がDBにないため、
外部キー違反で失敗
em.persist(address2);
tx.commit();
2012/4/24 Beginning JavaEE6 勉強会(2) 46
47. 4.2.3 エンティティの操作(7)
• イベントのカスケード
Customer customer = new Customer();
Address address = new Address("1", "2");
customer.setAddress(address);
tx.begin();
em.persist(customer);
tx.commit(); めんどくさいのでこう書きたい
※addressのpersistを省略したい
@Entity
public class Customer {
@Id persist(), remove()の際にカスケード
private Long id;
@OneToOne(fetch = FetchType.LAZY,
cascade = {CascadeType.PERSIST, CascadeType.REMOVE})
@JoinColumn(name = "address_fk", nullable = false)
private Address address;
}
2012/4/24 Beginning JavaEE6 勉強会(2) 47
48. 4.2.3 エンティティの操作(8)
• 二次キャッシュ機能
– 全てのディストリビューションで実装済み
• 内部の振る舞いは微妙に違ってくる
– 異なるEntityManagerで発行されたクエリについて
もマージしてキャッシュ
– 振る舞いをpersistence.xmlで設定
shared-cache-mode 振る舞い
ALL 全てのエンティティをキャッシュ
DISABLE_SELECTIVE @Cachable(false)以外のエンティティを
キャッシュ
ENABLE_SELECTIVE @Cachable(true)のエンティティのみ
キャッシュ
NONE 全てのエンティティをキャッシュしない
UNSPECIFIED ディストリビューションお任せ
2012/4/24 Beginning JavaEE6 勉強会(2) 48
49. 4.3 JPQL
• ほぼSQL
ggrks
@making
スライド作るの
心折れました…
2012/4/24 Beginning JavaEE6 勉強会(2) 49
50. 4.4 クエリ
• 4種類の発行方式
発行方式 説明
動的クエリ 必要なときにJPQLを都度投げる
名前付きクエリ 名前付きのクエリをあらかじめ作っておく
ネイティブクエリ 生SQLを投げる
Criteria API オブジェクト指向っぽい特殊な記法
• クエリの手順
– クエリの生成
• EntityManagerインタフェースのメソッドで生成
– クエリの発行
• 当該結果を全て取得するgetResultList()
• 当該結果を1つだけ取得するgetSingleResult()
2012/4/24 Beginning JavaEE6 勉強会(2) 50
51. 4.4.1 動的クエリ
@Entity
public class Customer {
@Id
private Long id;
private String name;
}
動的なパラメータを指定可能
String jpql =
"SELECT c FROM Customer c WHERE c.name = :name";
TypedQuery<Customer> query = 扱う型を指定可能
em.createQuery(jpql, Customer.class);
query.setParameter(“name", "making"); クエリの発行
List<Customer> customers = query.getResultList();
2012/4/24 Beginning JavaEE6 勉強会(2) 51
52. 4.4.2 名前付きクエリ
@Entity
@NamedQueries({
@NamedQuery(name = "Customer.findByName",
query = "SELECT c FROM Customer c WHERE c.name = :name")
})
public class Customer {
public static final String FIND_BY_NAME = "Customer.findByName"
@Id
private Long id; クエリ名を定数にしておくといい
private String name;
}
メソッドチェーンで設定可能
Query query = em.createNamedQuery(Customer.FIND_BY_NAME);
query.setParameter("name", "making").setMaxResults(3);
List<?> customers = query.getResultList();
取得件数を指定可能
2012/4/24 Beginning JavaEE6 勉強会(2) 52
53. 4.4.3 ネイティブクエリ
JPQLではCustomer
@Entity
@NamedQuery(name = "Customer.findAll",
query = "SELECT * FROM CUST")
@Table(name = "CUST")
public class Customer {
public static final String FIND_ALL =
"Customer.findAll"
@Id
private Long id;
private String name;
}
Query query = em.createNativeQuery(Customer.FIND_ALL);
List<?> customers = query.getResultList();
2012/4/24 Beginning JavaEE6 勉強会(2) 53
54. 4.4.4 Criteria API(オブジェクト指向クエリ)
• オブジェクト指向っぽくクエリを生成
– 文字列でゴリゴリ書くよりもバグを埋め込みにくい
• でもめんどくさい…
SELECT c FROM Customer c WHERE c.name = 'making'
CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaQuery<Customer> query =
builder.createQuery(Customer.class);
Root<Customer> c = query.from(Customer.class);
query.select(c).where(builder.equal(c.get("name"), "making"));
2012/4/24 Beginning JavaEE6 勉強会(2) 54
55. 4.5 同時実行
• 2種類のロック方式
ロック方式 説明
楽観的ロック 基本的に競合は発生しないという前提。
JPAではバージョニングの考え方を導入し、バージョン
違反がなければ同時実行を許容する。
悲観的ロック 基本的に競合は発生するという前提。
データベースは通常悲観ロックを提供する。書き込み
の発生が前提となる読み取りの際には、事前にロック
をかける。JPAではバージョニング対象外は悲観ロック
となる。
2012/4/24 Beginning JavaEE6 勉強会(2) 55
56. 4.5 バージョニングと楽観的ロック
• バージョニング用の属性を導入
@Entity
書き込み時に自動インクリメント
public class Book {
@Id 更新の際、データベースでの値と
private Long id; 異なる場合は例外となる
@Version
private Integer version;
}
tx1.begin(); tx2.begin();
Book book = em.find(Book.class, 10); Book book = em.find(Book.class, 10);
// バージョン1 // バージョン1
book.raisePriceByTwoDollars(); book.raisePriceByTwoDollars();
tx1.commit(); tx2.commit();
// バージョン2 // bookはバージョン1、DBはバージョン2
// よってOptimisticLockException
2012/4/24 Beginning JavaEE6 勉強会(2) 56
57. 第5章
コールバックとリスナ
2012/4/24 Beginning JavaEE6 勉強会(2) 57
58. 5.2 コールバック(1)
• CRUD操作に対して事前/事後処理を指定
customer cust = new Customer() カーベジコレクション完了
データベースから削除されても
メモリ上には存在
メモリ内に存在
customer cust = em.find()
@PostLoad
@PrePersist @PostRemove
em.persist(cust)
em.clear() @PostPersist
@PreRemove
em.detach(cust) em.remove(cust)
分離状態 管理状態 削除済み状態
@PreUpdate
em.merge(cust)
@PostUpdate, @PostLoad
@PreUpdate
em.refresh(cust) セッターによる更新
@PostLoad @PostUpdate
2012/4/24 Beginning JavaEE6 勉強会(2) 58
59. 5.2 コールバック(2)
@Entity
public class Customer {
@Id
private Long id;
private String name;
@Transient
private Integer age;
@PrePersist 書き込み前に名前のバリデーション
@PreUpdate
private void validate() {
if (c.getName() == null) {
throw new IllegalNameException();
}
}
@PostLoad 読み込み/書き込み後に年齢計算
@PostPersist
@PostUpdate
public void calcAge() {
// 年齢計算
age = ...
}
}
2012/4/24 Beginning JavaEE6 勉強会(2) 59
60. 5.2 コールバック(3)
• 注意点
– 1つのメソッドに複数のコールバックアノテーショ
ンは付加できる
– 同じエンティティ内に同一のコールバックアノテー
ションは複数付加できない
– 継承関係がある場合、親クラスのコールバックメ
ソッドが先に実行される
2012/4/24 Beginning JavaEE6 勉強会(2) 60
61. 5.3 リスナ(1)
• エンティティ外にコールバックメソッドを定義
する場合に利用
引数に呼び出しもとのエンティティ
public class DataValidationListener {
@PrePersist
@PreUpdate
public void validate(Customer c) {
if (c.getName() == null) {
throw new IllegalNameException();
}
}
}
@EntityListeners({DataValidationListener.class})
@Entity
public class Customer {
@Id 関連づけるリスナを配列で指定
private Long id; この順番でコールバックメソッドを実施
private String name;
}
2012/4/24 Beginning JavaEE6 勉強会(2) 61