🍌

JPAの@Inheritance(継承マッピング)とは

2022/05/30に公開

継承関係マッピング

  • 関係型DBには継承関係が存在しないです。
  • SuperタイプーSubタイプ関係がオブジェクトの継承と一番類似しています。
  • つまり、継承関係マッピングとはオブジェクトの継承関係をRDBのSuperタイプーSubタイプ関係にマッピングする事です。

三つの戦略

具現化方法には三つの戦略が存在します。
@Inheritance(strategy=InheritanceType.XXX)
* JOINED : Join戦略
* SINGLE_TABLE : 単一テーブル戦略
* TABLE_PER_CLASS : クラス別テーブル戦略

@DiscriminatorColumn (区分カラム)

@Entity
@Getter
@Inheritance(strategy = InheritanceType.JOINED)
@DiscriminatorColumn
public abstract class Item { ... }
  • 必須で入れた方がいいです。
  • Superタイプにつける
  • 属性
    • name : @DiscriminatorColumn(name=“DTYPE”) カラム名を指定可能
      • 基本値 : DTYPE
  • SINGLE_TABLE 戦略使う場合 @DisciriminatorColumn 宣言しなくても自動的にDTYPEカラムが追加されカラムの値としてはSubタイプのエンティティ名が入ります。

@DiscriminatorValue

  • 区分カラムに入る値を指定
  • Subタイプの方につける
  • 基本値 : エンティティ名が入ります。
    • @DiscriminatorValue(“XXX”) : 名前指定可能
@Entity
@DiscriminatorValue("BOOK")
public class Book extends Item { ... }

@Inheritance

  • 継承関係マッピングで一番重要なアノテーション。
  • 三つの戦略を提供する
  • @Inheritance(strategy=InheritanceType.XXX)
    • JOINED : Join戦略
    • SINGLE_TABLE : 単一テーブル戦略
    • TABLE_PER_CLASS : クラス別テーブル戦略
  • strategy指定がない場合基本SINGLE_TABLE戦略を使用
@Entity
@Getter
@Inheritance // strategy指定がない場合基本SINGLE_TABLE戦略を使用
@DiscriminatorColumn
public class Item extends BaseEntity{
    @Id
    @Column(name = "item_id")
    private Long id;
    private String name;
    private Integer price;
}

Join戦略(InheritanceType.JOINED)

@Entity
@Inheritance(strategy = InheritanceType.JOINED)
@Getter @Setter
@DiscriminatorColumn(name = "dtype") // default = "DTYPE"
public abstract class Item {
    @Id
    @Column(name = "item_id")
    private Long id;
    private String name;
    private Integer price;
}
@Entity
@DiscriminatorValue("A")
@Getter @Setter
public class Alcohol extends Item{
    private String level;
}
  • 一番正規化された戦略です。
  • 子テーブル識別子はPKでありながらFKです。
  • JOINED戦略は抽象クラスで宣言してもテーブルが生成される。
    • 親クラスだけでインスタンスを生成して何かをやるのなら抽象クラスではなく一般クラスで作るべきです。
  • 一番基本として使わなければならない戦略です。
  • ビジネス的に重要で複雑ならJOIN戦略を選ぶのが良いです。
  • Subタイプにも@Entity宣言必須です。
  • 区分カラムが無くてもJOINED戦略が具現化されるが区分及び運用の効率性のためDTYPE(@DiscriminatorColumn)を入れた方がいい
    • @DiscriminatorColumn省略すると DTYPEカラムは生成されないです。

Join戦略の長所

  • テーブルの正規化
  • 正規化を通じた重複防止、保存空間の効率化
  • 外来キー制約の活用ができます。

Join戦略の短所

  • 照会するときJOINを多く使います、それにより性能的に問題があり可能性があります。
  • 照会クエリがすごく複雑になる可能性があります。
  • テータ保存時insertクエリが2回投げられます。
Hibernate: 
    /* insert jpabasic.Snack
        */ insert 
        into
            Item
            (name, price, dtype, item_id) 
        values
            (?, ?, 'S', ?)
Hibernate: 
    /* insert jpabasic.Snack
        */ insert 
        into
            Snack
            (texture, item_id) 
        values
            (?, ?)

単一テーブル戦略(InheritanceType.SINGLE_TABLE)

  • @Inheritanceのみ宣言するとSINGLE_TABLE戦略を使用する、JPAの基本戦略であります。
  • 本当に単純で拡張の可能性が低い時にSINGLE_TABLE戦略を使用することが良いです。
  • 区分カラムは必須です。
    • @DiscriminatorColumnを宣言しなくてもHibernateが自動的に指定する。

単一テーブル戦略の長所

  • JOINが要らないので一般的に照会性能が早いです。
  • 照会クエリが単純です。
  • JOINEDとは違い当たり前だがinsertが一回だけ投げられる。

単一テーブル戦略の短所

  • 子エンティティをマッピングしたテーブルのカラムは全てnullを許さなければならない。
  • 単一テーブルに全てを入れるのでテーブルが大きくなりがちです、それにより状況によっては照会性能がむしろ遅くなる可能性もあります。

クラス別テーブル戦略(InheritanceType.TABLE_PER_CLASS)

  • SUPER TYPEを必ず抽象(abstract)クラスに作らなければならない。
    • 抽象クラスではなく一般クラスである場合JPAがSUPER TYPEもテーブルを作ってしまう
  • この戦略は非推奨である。

クラス別テーブル戦略長所

  • SUB TYPEを明確に区分して処理するとき効率的であります。
  • not null制約が使えます。

クラス別テーブル戦略短所

  • SUPER TYPEで照会する時unionを利用して全てのテーブルを探らなければならない。すごく非効率的である。
  • 幾つの子テーブルを一緒に照会する時性能が悪い
  • 子テーブルを合わしてクエリすることが難しい
  • GenerationType.IDENTITY戦略を使用することが出来ない。
    • GenerationType.IDENTITYはDBにキー生成を任せる戦略であるがTABLE_PER_CLASSに使用すると格テーブル毎にIDが生成されUNIQUEである事を担保できなくなりエラーが出る。なので他のキー生成戦略であるSEQUENCEやTABLE戦略を使うことでSUB TYPEのエンティティたちが同一のSEQUENCEを参照することでUNIQUE性を担保できるようにしなければならない。

(次)JPAのProxy

https://zenn.dev/dev_yoon/articles/83bdd4911d89b4

Discussion