Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

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

10,847 views

Published on

Published in: Technology
  • Be the first to comment

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

×