SlideShare a Scribd company logo
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

PPT
JPA説明会
PDF
金魚本読書ノート JPA編
PDF
CodableのためのEncoderとDecoderを 自作してみた
PDF
ATN No.2 Scala事始め
PDF
DSL by JRuby at JavaOne2012 JVM language BoF #jt12_b101
PDF
リーンなコードを書こう:実践的なオブジェクト指向設計
PDF
ドメイン駆動設計(DDD)の実践Part2
PDF
ScalaMatsuri 2016
JPA説明会
金魚本読書ノート JPA編
CodableのためのEncoderとDecoderを 自作してみた
ATN No.2 Scala事始め
DSL by JRuby at JavaOne2012 JVM language BoF #jt12_b101
リーンなコードを書こう:実践的なオブジェクト指向設計
ドメイン駆動設計(DDD)の実践Part2
ScalaMatsuri 2016

Viewers also liked (11)

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

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

PPT
Gaej Jdo
PDF
Entity Framework
PPTX
はじめてのJPA
PDF
Spring Data in a Nutshell
PPT
Spring3.1概要 データアクセスとトランザクション処理
PDF
Java女子部Java EEハンズオン(応用編)ソースコード
PDF
Introduction to cocoa sql mapper
PDF
Javaでmongo db
PPT
S2dao Seminar in tripodworks
PDF
Integral - New O/R Mapper for Common Lisp
PDF
VSUG Day 2010 Summer - Using ADO.NET Entity Framework
PDF
Mongo db + xsd:xml(20130219)
PDF
SQLアンチパターン - 開発者を待ち受ける25の落とし穴 (拡大版)
PDF
Sql基礎の基礎
PPTX
1.2新機能と1.2から始めるcql3
PPT
Springでdao 20070413
PDF
Sq lvs sql
PDF
よろしい、ならばMicro-ORMだ
PDF
MySQL 入門的なはなし
PDF
Daisukei vsug ef
Gaej Jdo
Entity Framework
はじめてのJPA
Spring Data in a Nutshell
Spring3.1概要 データアクセスとトランザクション処理
Java女子部Java EEハンズオン(応用編)ソースコード
Introduction to cocoa sql mapper
Javaでmongo db
S2dao Seminar in tripodworks
Integral - New O/R Mapper for Common Lisp
VSUG Day 2010 Summer - Using ADO.NET Entity Framework
Mongo db + xsd:xml(20130219)
SQLアンチパターン - 開発者を待ち受ける25の落とし穴 (拡大版)
Sql基礎の基礎
1.2新機能と1.2から始めるcql3
Springでdao 20070413
Sq lvs sql
よろしい、ならばMicro-ORMだ
MySQL 入門的なはなし
Daisukei vsug ef
Ad

More from Kinji Akemine (12)

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