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