Beginning Java EE 6 勉強会(2) #bje_study

10,039 views
9,876 views

Published on

Published in: Technology
0 Comments
21 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
10,039
On SlideShare
0
From Embeds
0
Number of Embeds
982
Actions
Shares
0
Downloads
159
Comments
0
Likes
21
Embeds 0
No embeds

No notes for slide

Beginning Java EE 6 勉強会(2) #bje_study

  1. 1. Beginning Java EE 6 勉強会(2) -JPA- 担当者:@kjstylepp Kinji Akemine 2012/04/25
  2. 2. 目次3. オブジェクト・リレーショナル・マッピング4. 永続オブジェクトの管理5. コールバックとリスナ 2012/4/24 Beginning JavaEE6 勉強会(2) 2
  3. 3. 本論に入る前に質問• 前回の勉強会で、代表的なORMソリューション が列挙されていましたが、全部言えますか? ggrks @making2012/4/24 Beginning JavaEE6 勉強会(2) 3
  4. 4. 第3章 オブジェクト・リレーショナル・ マッピング2012/4/24 Beginning JavaEE6 勉強会(2) 4
  5. 5. 3. オブジェクト・リレーショナル・マッピング• なぜORM? – ObjectとRDBはパラダイムが違う • リレーション – Object:参照、ポインタなどで表現 – RDB:外部キーで表現 • 継承 – RDBにはない概念 …etc – このへんの差をうまく吸収してあげる仕組みをORM が提供する2012/4/24 Beginning JavaEE6 勉強会(2) 5
  6. 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. 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. 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. 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. 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. 11. 3.2.2 主キー(1) @Id, @GeneratedValue• 主キーとしたい属性には@Idアノテーション• 主キーの値の生成方法を@GeneratedValueアノ テーションで指定 @GeneratedValue 振る舞い (strategy = ***) GenerationType.SEQUENCE シーケンス番号を利用する GenerationType.IDENTITY ID列の値を利用する GenerationType.TABLE 主キー用のテーブルを作成して利用する GenerationType.AUTO RDBMSに最適の方法でよろしくする 普通はこれでOK2012/4/24 Beginning JavaEE6 勉強会(2) 11
  12. 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. 13. 3.2.3 属性(1) @Basic• 属性とカラムのマッピング方法の基本的なオプ ション設定を行う – fetch = FetchType.LAZY • サイズが大きい場合などに利用し、遅延取得する • 属性のgetterメソッドが呼ばれたときに取得 – optional = false • persist時などに、属性がnullの場合にはじく2012/4/24 Beginning JavaEE6 勉強会(2) 13
  14. 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. 15. 3.2.3 属性(3) @Temporal, @Transient• Date型属性のマッピング @Temporal(***) 振る舞い TemporalType.DATE 年月日のみ扱う TemporalType.TIME 時分秒のみ扱う TemporalType.TIMESTAMP 年月日と時分秒の両方を扱う• 永続化したくない属性 – @Transientアノテーションを付加することで永続化 対象から除外2012/4/24 Beginning JavaEE6 勉強会(2) 15
  16. 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. 17. 3.2.5 基本型のコレクション• リレーションを意識せずマッピング可能@Entitypublic 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. 18. 3.2.6 基本型のマッピング• Map構造も用意にマッピング可能@Entitypublic 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
  19. 19. 3.3 XMLによるマッピング• アノテーションと同等の設定が可能 – 普通はアノテーション – 解釈はXMLのほうが優先される ggrks @making2012/4/24 Beginning JavaEE6 勉強会(2) 19
  20. 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
  21. 21. 3.5 リレーションシップ・マッピング• 複雑なオブジェクト構造をデータベースのリ レーションとしてマッピング しっかり やれよ @making2012/4/24 Beginning JavaEE6 勉強会(2) 21
  22. 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 132012/4/24 Beginning JavaEE6 勉強会(2) 22
  23. 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. 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. 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 integer2012/4/24 Beginning JavaEE6 勉強会(2) 25
  26. 26. 3.5.2 エンティティのリレーションシップ(3) @OneToMany、一方向 • カラム結合に変更可能 こっちは変更なし@Entity @Entitypublic 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. 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
  28. 28. 3.5.3 リレーションシップのフェッチ• オブジェクト、データベースへのアクセス頻度 を考慮して適切に設定する必要あり• デフォルトのフェッチ方式 カーディナリ フェッチ方 ティ 式 @OneToOne EAGER @ManyToOne EAGER @OneToMany LAZY @ManyToMany LAZY2012/4/24 Beginning JavaEE6 勉強会(2) 28
  29. 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. 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. 31. 3.6 継承のマッピング• 3つのマッピング方式 – クラス階層ごとに1つのテーブル • 全部まとめて1つのテーブル • デフォルトはこの方式 – 結合サブクラス • クラス間の差分の属性のみを別テーブル – 具象クラスごとのテーブル • 継承する属性も含め、クラスごとにテーブル • オプション機能なのでディストリビューションによっては 実装されていない可能性あり?2012/4/24 Beginning JavaEE6 勉強会(2) 31
  32. 32. 3.6.1 継承方式(1) 例題• 以下の例題をもとにマッピング方法を説明 <<エンティティ>> Item -id : Long -title : String <<エンティティ>> <<エンティティ>> Book CD -isbn : String -musicCompany : String2012/4/24 Beginning JavaEE6 勉強会(2) 32
  33. 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. 34. 3.6.1 継承方式(3)結合サブクラス• クラス間の差分の属性のみを別テーブル@Entity@Inheritance(strategy = InheritanceType.JOINED)public class Item { @Id protected Long id; 結合サブクラス方式でマッピング protected String title;}@Entity @Entitypublic 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 Strin2012/4/24 Beginning g JavaEE6 勉強会(2) 34
  35. 35. 3.6.1 継承方式(4)具象クラスごとのテーブル• 継承する属性も含め、クラスごとにテーブル@Entity@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)public class Item { @Id protected Long id; 具象クラスごとの方式でマッピング protected String title;}@Entity @Entitypublic 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 g2012/4/24 Strin Beginning JavaEE6 勉強会(2) Strin 35
  36. 36. 3.6.2 継承階層に含まれるクラスの種類(1)• 抽象エンティティ – 特に気にせず大丈夫• 非エンティティ – 特に気にせず大丈夫 – ただし(当たり前だが)非エンティティの属性は マッピング対象にはならない• マップドスーパークラス – 非エンティティではあるが、継承先のクラスにマッ ピング情報を引き継ぎたい場合に利用2012/4/24 Beginning JavaEE6 勉強会(2) 36
  37. 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 Strin2012/4/24 g Beginning JavaEE6 勉強会(2) 37
  38. 38. 第4章 永続オブジェクトの管理2012/4/24 Beginning JavaEE6 勉強会(2) 38
  39. 39. 4.1 エンティティをクエリする方法• 全ての操作はEntityManager経由 – PersistenceからEntityManagerFactoryを作成 • データーベースへの接続情報はpersistence.xmlに – FactoryからEntityManagerを作成 – Managerに対してCRUD操作2012/4/24 Beginning JavaEE6 勉強会(2) 39
  40. 40. 4.2 エンティティ・マネージャ• エンティティ・マネージャの取得 – JavaSE環境 • 作成からクローズまで実装する必要あり – JavaEE環境 • アノテーション等を用いて簡単に取得可能• 永続性コンテキスト – EntityManagerで管理しているインスタンスの集合 • Manager内に同じIDのものは存在しない – Managerは同時に複数存在できるので、違うManager間 で同じIDのインスタンスは存在しえる • データベースへflushされるまでの一時キャッシュ2012/4/24 Beginning JavaEE6 勉強会(2) 40
  41. 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. 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. 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. 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. 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. 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. 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. 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. 49. 4.3 JPQL• ほぼSQL ggrks @making スライド作るの 心折れました…2012/4/24 Beginning JavaEE6 勉強会(2) 49
  50. 50. 4.4 クエリ• 4種類の発行方式 発行方式 説明 動的クエリ 必要なときにJPQLを都度投げる 名前付きクエリ 名前付きのクエリをあらかじめ作っておく ネイティブクエリ 生SQLを投げる Criteria API オブジェクト指向っぽい特殊な記法• クエリの手順 – クエリの生成 • EntityManagerインタフェースのメソッドで生成 – クエリの発行 • 当該結果を全て取得するgetResultList() • 当該結果を1つだけ取得するgetSingleResult()2012/4/24 Beginning JavaEE6 勉強会(2) 50
  51. 51. 4.4.1 動的クエリ@Entitypublic 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. 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. 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. 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. 55. 4.5 同時実行• 2種類のロック方式 ロック方式 説明 楽観的ロック 基本的に競合は発生しないという前提。 JPAではバージョニングの考え方を導入し、バージョン 違反がなければ同時実行を許容する。 悲観的ロック 基本的に競合は発生するという前提。 データベースは通常悲観ロックを提供する。書き込み の発生が前提となる読み取りの際には、事前にロック をかける。JPAではバージョニング対象外は悲観ロック となる。2012/4/24 Beginning JavaEE6 勉強会(2) 55
  56. 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 // バージョン1book.raisePriceByTwoDollars(); book.raisePriceByTwoDollars();tx1.commit(); tx2.commit();// バージョン2 // bookはバージョン1、DBはバージョン2 // よってOptimisticLockException2012/4/24 Beginning JavaEE6 勉強会(2) 56
  57. 57. 第5章 コールバックとリスナ2012/4/24 Beginning JavaEE6 勉強会(2) 57
  58. 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 @PostUpdate2012/4/24 Beginning JavaEE6 勉強会(2) 58
  59. 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. 60. 5.2 コールバック(3)• 注意点 – 1つのメソッドに複数のコールバックアノテーショ ンは付加できる – 同じエンティティ内に同一のコールバックアノテー ションは複数付加できない – 継承関係がある場合、親クラスのコールバックメ ソッドが先に実行される2012/4/24 Beginning JavaEE6 勉強会(2) 60
  61. 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
  62. 62. 5.3 リスナ(2)• 全てのエンティティから使用できるリスナ(デ フォルトリスナ) – XMLマッピングファイルを利用 – 特定のエンティティだけ外したい場合は @ExcludeDefaultListenersアノテーションを利用2012/4/24 Beginning JavaEE6 勉強会(2) 62
  63. 63. 2012/4/24 Beginning JavaEE6 勉強会(2) 63

×