SlideShare a Scribd company logo
1 of 63
Beginning Java EE 6 勉強会(2)
                      -JPA-

                  担当者:@kjstylepp
                     Kinji Akemine
                        2012/04/25
目次
3.      オブジェクト・リレーショナル・マッピング
4.      永続オブジェクトの管理
5.      コールバックとリスナ


     2012/4/24   Beginning JavaEE6 勉強会(2)   2
本論に入る前に質問
• 前回の勉強会で、代表的なORMソリューション
  が列挙されていましたが、全部言えますか?

                           ggrks




                @making




2012/4/24   Beginning JavaEE6 勉強会(2)   3
第3章
            オブジェクト・リレーショナル・
            マッピング




2012/4/24    Beginning JavaEE6 勉強会(2)   4
3. オブジェクト・リレーショナル・マッピング
• なぜORM?
     – ObjectとRDBはパラダイムが違う
            • リレーション
               – Object:参照、ポインタなどで表現
               – RDB:外部キーで表現
            • 継承
               – RDBにはない概念
            …etc


     – このへんの差をうまく吸収してあげる仕組みをORM
       が提供する



2012/4/24            Beginning JavaEE6 勉強会(2)   5
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
3.1.1 Configuration-by-Exception
• 設定より規約
     – クラス名→テーブル名
            • Bookクラス→BOOKテーブル
            • 名前を変えたい場合は@Tableアノテーションを利用


     – 属性名→カラム名
            • id属性→IDカラム
            • 名前を変えたい場合は@Columnアノテーションを利用


     – 型マッピング規則は、基本規則は決まっているが、
       細かい部分はJDBCやRDBMSによって違う

     – 基盤のデータベース情報はpersistence.xmlに記述

2012/4/24           Beginning JavaEE6 勉強会(2)   7
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
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
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
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
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
3.2.3 属性(1) @Basic
• 属性とカラムのマッピング方法の基本的なオプ
  ション設定を行う
     – fetch = FetchType.LAZY
            • サイズが大きい場合などに利用し、遅延取得する
            • 属性のgetterメソッドが呼ばれたときに取得


     – optional = false
            • persist時などに、属性がnullの場合にはじく




2012/4/24            Beginning JavaEE6 勉強会(2)   13
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
3.2.3 属性(3) @Temporal, @Transient
• Date型属性のマッピング
                  @Temporal(***)                   振る舞い
            TemporalType.DATE            年月日のみ扱う

            TemporalType.TIME            時分秒のみ扱う

            TemporalType.TIMESTAMP       年月日と時分秒の両方を扱う




• 永続化したくない属性
     – @Transientアノテーションを付加することで永続化
       対象から除外


2012/4/24                   Beginning JavaEE6 勉強会(2)      15
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
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
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
3.3 XMLによるマッピング
• アノテーションと同等の設定が可能
     – 普通はアノテーション
     – 解釈はXMLのほうが優先される

                            ggrks




                 @making




2012/4/24    Beginning JavaEE6 勉強会(2)   19
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
3.5 リレーションシップ・マッピング
• 複雑なオブジェクト構造をデータベースのリ
  レーションとしてマッピング

                         しっかり
                          やれよ




                @making




2012/4/24   Beginning JavaEE6 勉強会(2)   21
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
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
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
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
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
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
3.5.3 リレーションシップのフェッチ
• オブジェクト、データベースへのアクセス頻度
  を考慮して適切に設定する必要あり

• デフォルトのフェッチ方式
             カーディナリ           フェッチ方
               ティ             式
            @OneToOne         EAGER
            @ManyToOne        EAGER
            @OneToMany        LAZY
            @ManyToMany       LAZY




2012/4/24         Beginning JavaEE6 勉強会(2)   28
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
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
3.6 継承のマッピング
• 3つのマッピング方式
     – クラス階層ごとに1つのテーブル
            • 全部まとめて1つのテーブル
            • デフォルトはこの方式


     – 結合サブクラス
            • クラス間の差分の属性のみを別テーブル


     – 具象クラスごとのテーブル
            • 継承する属性も含め、クラスごとにテーブル
            • オプション機能なのでディストリビューションによっては
              実装されていない可能性あり?



2012/4/24           Beginning JavaEE6 勉強会(2)   31
3.6.1 継承方式(1) 例題
• 以下の例題をもとにマッピング方法を説明

                         <<エンティティ>>
                             Item
                        -id : Long
                        -title : String




            <<エンティティ>>                          <<エンティティ>>
                Book                                CD
       -isbn : String                      -musicCompany : String




2012/4/24                Beginning JavaEE6 勉強会(2)                   32
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
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
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
3.6.2 継承階層に含まれるクラスの種類(1)
• 抽象エンティティ
     – 特に気にせず大丈夫


• 非エンティティ
     – 特に気にせず大丈夫
     – ただし(当たり前だが)非エンティティの属性は
       マッピング対象にはならない


• マップドスーパークラス
     – 非エンティティではあるが、継承先のクラスにマッ
       ピング情報を引き継ぎたい場合に利用


2012/4/24    Beginning JavaEE6 勉強会(2)   36
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
第4章
            永続オブジェクトの管理



2012/4/24   Beginning JavaEE6 勉強会(2)   38
4.1 エンティティをクエリする方法
• 全ての操作はEntityManager経由
     – PersistenceからEntityManagerFactoryを作成
            • データーベースへの接続情報はpersistence.xmlに
     – FactoryからEntityManagerを作成
     – Managerに対してCRUD操作




2012/4/24            Beginning JavaEE6 勉強会(2)   39
4.2 エンティティ・マネージャ
• エンティティ・マネージャの取得
     – JavaSE環境
            • 作成からクローズまで実装する必要あり
     – JavaEE環境
            • アノテーション等を用いて簡単に取得可能


• 永続性コンテキスト
     – EntityManagerで管理しているインスタンスの集合
            • Manager内に同じIDのものは存在しない
                – Managerは同時に複数存在できるので、違うManager間
                  で同じIDのインスタンスは存在しえる
            • データベースへflushされるまでの一時キャッシュ


2012/4/24            Beginning JavaEE6 勉強会(2)       40
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
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
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
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
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
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
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
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
4.3 JPQL
• ほぼSQL
                                  ggrks




                        @making




            スライド作るの
            心折れました…



2012/4/24         Beginning JavaEE6 勉強会(2)   49
4.4 クエリ
• 4種類の発行方式
             発行方式                         説明
        動的クエリ          必要なときにJPQLを都度投げる
        名前付きクエリ        名前付きのクエリをあらかじめ作っておく
        ネイティブクエリ 生SQLを投げる
        Criteria API   オブジェクト指向っぽい特殊な記法

• クエリの手順
     – クエリの生成
            • EntityManagerインタフェースのメソッドで生成
     – クエリの発行
            • 当該結果を全て取得するgetResultList()
            • 当該結果を1つだけ取得するgetSingleResult()


2012/4/24              Beginning JavaEE6 勉強会(2)   50
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
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
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
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
4.5 同時実行
• 2種類のロック方式
     ロック方式                       説明
   楽観的ロック 基本的に競合は発生しないという前提。

             JPAではバージョニングの考え方を導入し、バージョン
             違反がなければ同時実行を許容する。
   悲観的ロック 基本的に競合は発生するという前提。

             データベースは通常悲観ロックを提供する。書き込み
             の発生が前提となる読み取りの際には、事前にロック
             をかける。JPAではバージョニング対象外は悲観ロック
             となる。




2012/4/24       Beginning JavaEE6 勉強会(2)   55
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
第5章
            コールバックとリスナ



2012/4/24   Beginning JavaEE6 勉強会(2)   57
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
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
5.2 コールバック(3)
• 注意点
     – 1つのメソッドに複数のコールバックアノテーショ
       ンは付加できる
     – 同じエンティティ内に同一のコールバックアノテー
       ションは複数付加できない
     – 継承関係がある場合、親クラスのコールバックメ
       ソッドが先に実行される




2012/4/24   Beginning JavaEE6 勉強会(2)   60
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
5.3 リスナ(2)
• 全てのエンティティから使用できるリスナ(デ
  フォルトリスナ)
     – XMLマッピングファイルを利用
     – 特定のエンティティだけ外したい場合は
       @ExcludeDefaultListenersアノテーションを利用




2012/4/24       Beginning JavaEE6 勉強会(2)    62
2012/4/24   Beginning JavaEE6 勉強会(2)   63

More Related Content

Viewers also liked

Beginning Java EE 6 勉強会(4) #bje_study
Beginning Java EE 6 勉強会(4) #bje_studyBeginning Java EE 6 勉強会(4) #bje_study
Beginning Java EE 6 勉強会(4) #bje_study
George Okada
 
Beginning Java EE 6 勉強会(7) #bje_study
Beginning Java EE 6 勉強会(7) #bje_studyBeginning Java EE 6 勉強会(7) #bje_study
Beginning Java EE 6 勉強会(7) #bje_study
ikeyat
 
Beginning Java EE 6 勉強会(5) #bje_study
Beginning Java EE 6 勉強会(5) #bje_studyBeginning Java EE 6 勉強会(5) #bje_study
Beginning Java EE 6 勉強会(5) #bje_study
zuisener .
 
Beginning Java EE 6 勉強会(1) #bje_study
Beginning Java EE 6 勉強会(1) #bje_studyBeginning Java EE 6 勉強会(1) #bje_study
Beginning Java EE 6 勉強会(1) #bje_study
Toshiaki Maki
 

Viewers also liked (11)

Beginning Java EE 6 勉強会(4) #bje_study
Beginning Java EE 6 勉強会(4) #bje_studyBeginning Java EE 6 勉強会(4) #bje_study
Beginning Java EE 6 勉強会(4) #bje_study
 
Beginning Java EE 6 勉強会(6) #bje_study
Beginning Java EE 6 勉強会(6) #bje_studyBeginning Java EE 6 勉強会(6) #bje_study
Beginning Java EE 6 勉強会(6) #bje_study
 
About NoSQL
About NoSQLAbout NoSQL
About NoSQL
 
Beginning Java EE 6 勉強会(3) #bje_study
Beginning Java EE 6 勉強会(3) #bje_studyBeginning Java EE 6 勉強会(3) #bje_study
Beginning Java EE 6 勉強会(3) #bje_study
 
Beginning Java EE 6 勉強会(7) #bje_study
Beginning Java EE 6 勉強会(7) #bje_studyBeginning Java EE 6 勉強会(7) #bje_study
Beginning Java EE 6 勉強会(7) #bje_study
 
Beginning Java EE 6 勉強会(5) #bje_study
Beginning Java EE 6 勉強会(5) #bje_studyBeginning Java EE 6 勉強会(5) #bje_study
Beginning Java EE 6 勉強会(5) #bje_study
 
Beginning Java EE 6 勉強会(1) #bje_study
Beginning Java EE 6 勉強会(1) #bje_studyBeginning Java EE 6 勉強会(1) #bje_study
Beginning Java EE 6 勉強会(1) #bje_study
 
就活でお祈りされない たった3つのアドバイス
就活でお祈りされない たった3つのアドバイス就活でお祈りされない たった3つのアドバイス
就活でお祈りされない たった3つのアドバイス
 
Spring Framework ふりかえりと4.3新機能
Spring Framework ふりかえりと4.3新機能Spring Framework ふりかえりと4.3新機能
Spring Framework ふりかえりと4.3新機能
 
JPA Best Practices
JPA Best PracticesJPA Best Practices
JPA Best Practices
 
RDB技術者のためのNoSQLガイド NoSQLの必要性と位置づけ
RDB技術者のためのNoSQLガイド NoSQLの必要性と位置づけRDB技術者のためのNoSQLガイド NoSQLの必要性と位置づけ
RDB技術者のためのNoSQLガイド NoSQLの必要性と位置づけ
 

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

Spring Data in a Nutshell
Spring Data in a NutshellSpring Data in a Nutshell
Spring Data in a Nutshell
Tsuyoshi Miyake
 
プログラミング言語 Ruby 2章 Rubyプログラムの構造と実行
プログラミング言語 Ruby 2章 Rubyプログラムの構造と実行プログラミング言語 Ruby 2章 Rubyプログラムの構造と実行
プログラミング言語 Ruby 2章 Rubyプログラムの構造と実行
monglee
 
24時間でiOSアプリ-Twitterクライアント-の作成にチャレンジ ver1.1
24時間でiOSアプリ-Twitterクライアント-の作成にチャレンジ ver1.124時間でiOSアプリ-Twitterクライアント-の作成にチャレンジ ver1.1
24時間でiOSアプリ-Twitterクライアント-の作成にチャレンジ ver1.1
聡 中川
 
Msgpack cli-tech-aid-2013
Msgpack cli-tech-aid-2013Msgpack cli-tech-aid-2013
Msgpack cli-tech-aid-2013
Yusuke Fujiwara
 

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

2015-12-16 某S社、出直しDDDってるってよ
2015-12-16 某S社、出直しDDDってるってよ2015-12-16 某S社、出直しDDDってるってよ
2015-12-16 某S社、出直しDDDってるってよ
 
実践Realm
実践Realm実践Realm
実践Realm
 
Spring Data in a Nutshell
Spring Data in a NutshellSpring Data in a Nutshell
Spring Data in a Nutshell
 
BindableProperty 書くのクソダリーんだけど、 あいつなんやねん(仮)
BindableProperty書くのクソダリーんだけど、あいつなんやねん(仮)BindableProperty書くのクソダリーんだけど、あいつなんやねん(仮)
BindableProperty 書くのクソダリーんだけど、 あいつなんやねん(仮)
 
si-2. テーブル定義,データ型,主キー,SQL 問い合わせ
si-2. テーブル定義,データ型,主キー,SQL 問い合わせsi-2. テーブル定義,データ型,主キー,SQL 問い合わせ
si-2. テーブル定義,データ型,主キー,SQL 問い合わせ
 
Dsl&Builder
Dsl&BuilderDsl&Builder
Dsl&Builder
 
Springでdao 20070413
Springでdao 20070413Springでdao 20070413
Springでdao 20070413
 
プログラミング言語 Ruby 2章 Rubyプログラムの構造と実行
プログラミング言語 Ruby 2章 Rubyプログラムの構造と実行プログラミング言語 Ruby 2章 Rubyプログラムの構造と実行
プログラミング言語 Ruby 2章 Rubyプログラムの構造と実行
 
20181031 springfest spring data geode
20181031 springfest spring data geode20181031 springfest spring data geode
20181031 springfest spring data geode
 
Entity Framework
Entity FrameworkEntity Framework
Entity Framework
 
マルチテナントのアプリケーション実装〜実践編〜
マルチテナントのアプリケーション実装〜実践編〜マルチテナントのアプリケーション実装〜実践編〜
マルチテナントのアプリケーション実装〜実践編〜
 
Lombok java
Lombok javaLombok java
Lombok java
 
pi-15. カプセル化, MVCモデル, オブジェクトのマッピング
pi-15. カプセル化, MVCモデル, オブジェクトのマッピングpi-15. カプセル化, MVCモデル, オブジェクトのマッピング
pi-15. カプセル化, MVCモデル, オブジェクトのマッピング
 
DeclarativeSql
DeclarativeSqlDeclarativeSql
DeclarativeSql
 
Java8から17へ
Java8から17へJava8から17へ
Java8から17へ
 
メタデータスキーマレジストリ MetaBridge
メタデータスキーマレジストリ MetaBridgeメタデータスキーマレジストリ MetaBridge
メタデータスキーマレジストリ MetaBridge
 
Synthesijer - HLS frineds 20190511
Synthesijer - HLS frineds 20190511Synthesijer - HLS frineds 20190511
Synthesijer - HLS frineds 20190511
 
Scalaによるドメイン特化言語を使ったソフトウェアの動作解析
Scalaによるドメイン特化言語を使ったソフトウェアの動作解析Scalaによるドメイン特化言語を使ったソフトウェアの動作解析
Scalaによるドメイン特化言語を使ったソフトウェアの動作解析
 
24時間でiOSアプリ-Twitterクライアント-の作成にチャレンジ ver1.1
24時間でiOSアプリ-Twitterクライアント-の作成にチャレンジ ver1.124時間でiOSアプリ-Twitterクライアント-の作成にチャレンジ ver1.1
24時間でiOSアプリ-Twitterクライアント-の作成にチャレンジ ver1.1
 
Msgpack cli-tech-aid-2013
Msgpack cli-tech-aid-2013Msgpack cli-tech-aid-2013
Msgpack cli-tech-aid-2013
 

More from Kinji Akemine

ICST 2015 まるわかりDay! -Model
ICST 2015 まるわかりDay! -ModelICST 2015 まるわかりDay! -Model
ICST 2015 まるわかりDay! -Model
Kinji Akemine
 
テスト分析入門 -「ゆもつよメソッド」を例に- #wacate
テスト分析入門 -「ゆもつよメソッド」を例に- #wacateテスト分析入門 -「ゆもつよメソッド」を例に- #wacate
テスト分析入門 -「ゆもつよメソッド」を例に- #wacate
Kinji Akemine
 
TABOK Skill Category2解説
TABOK Skill Category2解説TABOK Skill Category2解説
TABOK Skill Category2解説
Kinji Akemine
 

More from Kinji Akemine (12)

WACATE2019冬 ソフトウェアテスト業界でのステップアップを考えよう #wacate
WACATE2019冬 ソフトウェアテスト業界でのステップアップを考えよう #wacateWACATE2019冬 ソフトウェアテスト業界でのステップアップを考えよう #wacate
WACATE2019冬 ソフトウェアテスト業界でのステップアップを考えよう #wacate
 
WACATE2018冬 45分で講師になれそうな気になるASTERセミナー標準テキスト #wacate
WACATE2018冬 45分で講師になれそうな気になるASTERセミナー標準テキスト #wacateWACATE2018冬 45分で講師になれそうな気になるASTERセミナー標準テキスト #wacate
WACATE2018冬 45分で講師になれそうな気になるASTERセミナー標準テキスト #wacate
 
テストの視点からのモデリング(公開用) #wacate
テストの視点からのモデリング(公開用) #wacateテストの視点からのモデリング(公開用) #wacate
テストの視点からのモデリング(公開用) #wacate
 
#wacate 2017 冬 ISONO:REBOOT -評価することにこだわろう-
#wacate 2017 冬 ISONO:REBOOT -評価することにこだわろう-#wacate 2017 冬 ISONO:REBOOT -評価することにこだわろう-
#wacate 2017 冬 ISONO:REBOOT -評価することにこだわろう-
 
60分でわかった気になるISO29119 #wacate
60分でわかった気になるISO29119 #wacate60分でわかった気になるISO29119 #wacate
60分でわかった気になるISO29119 #wacate
 
ICST 2015 まるわかりDay! -Model
ICST 2015 まるわかりDay! -ModelICST 2015 まるわかりDay! -Model
ICST 2015 まるわかりDay! -Model
 
モデル検査入門 #wacate
モデル検査入門 #wacateモデル検査入門 #wacate
モデル検査入門 #wacate
 
テスト分析入門 -「ゆもつよメソッド」を例に- #wacate
テスト分析入門 -「ゆもつよメソッド」を例に- #wacateテスト分析入門 -「ゆもつよメソッド」を例に- #wacate
テスト分析入門 -「ゆもつよメソッド」を例に- #wacate
 
モデルベースドテスト入門 -テスト詳細設計を自動化しよう- #stac2013
モデルベースドテスト入門 -テスト詳細設計を自動化しよう- #stac2013モデルベースドテスト入門 -テスト詳細設計を自動化しよう- #stac2013
モデルベースドテスト入門 -テスト詳細設計を自動化しよう- #stac2013
 
第1回キーワード駆動テスト勉強会
第1回キーワード駆動テスト勉強会第1回キーワード駆動テスト勉強会
第1回キーワード駆動テスト勉強会
 
Astahプラグイン開発勉強会
Astahプラグイン開発勉強会Astahプラグイン開発勉強会
Astahプラグイン開発勉強会
 
TABOK Skill Category2解説
TABOK Skill Category2解説TABOK Skill Category2解説
TABOK Skill Category2解説
 

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

  • 1. Beginning Java EE 6 勉強会(2) -JPA- 担当者:@kjstylepp Kinji Akemine 2012/04/25
  • 2. 目次 3. オブジェクト・リレーショナル・マッピング 4. 永続オブジェクトの管理 5. コールバックとリスナ 2012/4/24 Beginning JavaEE6 勉強会(2) 2
  • 3. 本論に入る前に質問 • 前回の勉強会で、代表的なORMソリューション が列挙されていましたが、全部言えますか? ggrks @making 2012/4/24 Beginning JavaEE6 勉強会(2) 3
  • 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
  • 19. 3.3 XMLによるマッピング • アノテーションと同等の設定が可能 – 普通はアノテーション – 解釈はXMLのほうが優先される ggrks @making 2012/4/24 Beginning JavaEE6 勉強会(2) 19
  • 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. 3.5 リレーションシップ・マッピング • 複雑なオブジェクト構造をデータベースのリ レーションとしてマッピング しっかり やれよ @making 2012/4/24 Beginning JavaEE6 勉強会(2) 21
  • 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
  • 28. 3.5.3 リレーションシップのフェッチ • オブジェクト、データベースへのアクセス頻度 を考慮して適切に設定する必要あり • デフォルトのフェッチ方式 カーディナリ フェッチ方 ティ 式 @OneToOne EAGER @ManyToOne EAGER @OneToMany LAZY @ManyToMany LAZY 2012/4/24 Beginning JavaEE6 勉強会(2) 28
  • 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
  • 39. 4.1 エンティティをクエリする方法 • 全ての操作はEntityManager経由 – PersistenceからEntityManagerFactoryを作成 • データーベースへの接続情報はpersistence.xmlに – FactoryからEntityManagerを作成 – Managerに対してCRUD操作 2012/4/24 Beginning JavaEE6 勉強会(2) 39
  • 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
  • 62. 5.3 リスナ(2) • 全てのエンティティから使用できるリスナ(デ フォルトリスナ) – XMLマッピングファイルを利用 – 特定のエンティティだけ外したい場合は @ExcludeDefaultListenersアノテーションを利用 2012/4/24 Beginning JavaEE6 勉強会(2) 62
  • 63. 2012/4/24 Beginning JavaEE6 勉強会(2) 63