상속 관계 매핑

상속관계를 테이블로 구현하는 방법

  • 슈퍼타입-서브타입 관계 논리 모델을 다음 3가지 방법의 물리적 모델링 기법으로 설계 가능

  • 상속관계 매핑 : 객체의 상속 구조를 DB의 슈퍼타입 서브타입 관계로 매핑

  1. 조인 전략 : 중복되는 필드 없이 굉장히 정규화된 방식의 테이블 설계 가능.

  2. 단일 테이블 전략 : 단순하고 일반적으로 성능이 좋다.

  3. 구현 클래스마다 테이블 생성 전략

JPA는 어떤 방법으로 설계하든 전략만 바꿈으로써 다 구현 가능하다.

  • @Inheritance(strategy=inheritanceType.XXX) JOINED : 조인 전략 SINGLE_TABLE : 단일 테이블 전략 TABLE_PER_CLASS : 구현 클래스마다 테이블 전략

  • @DiscriminatorColumn(name=“DTYPE”) : 부모

  • @DiscriminatorValue(“XXX”) : 자식

  1. JPA Defalut :조인 전략(정석)

//Item
@Entity
public class Item {
    @Id @GeneratedValue
    private Long id;

    private String name;
    private int price;
}
//1.Album
package hellojpa;

import javax.persistence.Entity;

@Entity
public class Album extends Item{

    private String artist;
}
//2. Movie
package hellojpa;

import javax.persistence.Entity;

@Entity
public class Movie extends Item{
    private String director;
    private String actor;

}
//3. Book
package hellojpa;

import javax.persistence.Entity;

@Entity
public class Book extends Item{
    private String author;
    private int isbn;
}

장점

  • 테이블 정규화

  • 외래 키 참조 무결성 제약조건 활용가능 =>다른 테이블에서 상품에 대한 데이터를 가져올 때 ITEM 테이블만 조회하면 된다. ex) 상품 정산 시스

  • 저장공간 효율화

단점(크게 단점은 아니지만 단일 테이블과 비교했을 때!) =>조인을 최적화해서 사용하면 성능에 영향을 주지 않을 수도 있다.

  • 데이터 저장시 INSERT SQL 2번 호출

  • 조회 쿼리가 복잡함. 조회시 조인을 많이 사용, 성능 저하

package hellojpa;

import javax.persistence.*;

@Entity
@Inheritance(strategy = InheritanceType.JOINED)
public class Item {
    @Id @GeneratedValue
    private Long id;

    private String name;
    private int price;

}

DB에 저장된 모습 ITEM의 ID와 MOVIE의 ID는 동일하다. ITEM의 ID는 PK이고, MOVIE의 ID는 PK이면서 동시에 FK이다!

Movie 데이터를 가져오려면 Item이랑 조인해서 가져온다! 왜냐하면 Movie는 Item을 상속받았기 때문이다!

이렇게 상속관계의 테이블에 데이터를 저장할 때나 조회할 때 JPA가 알아서 저장시에 INSERT도 두번해주고, 조회 시에는 JOIN해준다.

@DiscriminatorColumn

  • 조인 전략에서는 조인을 해보면 ITEM 테이블에서 어떤 자식 테이블이 들어와서 데이터가 저장됐는지 알 수 있기 때문에 없어도 상관은 없지만 구분하기 위해 있는 것이 좋다!

  • 싱글테이블 전략에서는 필수!

  • 부모 클래스에서 자식 클래스 아이템 구분

  • 상속받은 객체들을 구분하는 식별자는 해당 객체의 이름이 디폴트로 들어간다!

자식 클래스에서 이름 설정 : @DiscriminatorValue("A")

@Entity
@Inheritance(strategy = InheritanceType.JOINED)
@DiscriminatorColumn(name="dtype")

public class Item {

2. 싱글 테이블 전략 : 성능 GOOD👍🏻 INSERT도 한번에 모든 필드가 들어가고, JOIN을 할 필요도 없기 때문에 성능이 좋다! @DiscriminatorColumn이 없어도 자동으로 "DTYPE" 필드가 필수로 생성되어 들어간다!

단점

  • 자식 엔티티가 매핑한 컬럼은 모두 null 허용 ex)Book인경우 ARTIST, DIRECTOR는 null

    데이터 무결성에 있어서는 좋지 않다.

  • 단일 테이블에 모든 것을 저장하므로 테이블이 커질 수 있다.

  • 상황에 따라서 조회 성능이 오히려 느려질 수 있다. (실제로는그 임계점을 넘는 경우는 잘 없다.)

3. 구현 클래스마다 테이블 생성 전략(쓰면 안됨❌) ex) 정산시스템 단일 테이블 : 한 테이블에 NAME, PRICE 있기 때문에 한번에 정산 가능 조인 전략 : ITEM 테이블에 NAME, PRICE 있기 때문에 한번에 정산 가능 구현 클래스마다 테이블 생성 전략 : 엔티티마다 다 따로 정산 로직 돌려야하고, 새로운 엔티티 추가될 때마다 코드를 다 수정해야함!

부모 개념인 Item 클래스의 테이블은 안 만들어진다. Item 클래스는 추상클래스로 만들어야한다. 그렇지 않으면 Item 클래스만을 사용하기 위해 Item 테이블이 생성된다. 클래스마다 테이블이 다 따로 생성되기 때문에 @DiscriminatorColumn는 무의미하다.

장점

  • 서브 타입을 명확하게 구분해서 처리할 때 효과적

  • not null 제약조건 사용 가능

단점

  • 자식 테이블을 통합해서 쿼리하기 어려움

  • 여러 자식 테이블을 함께 조회할 때 성능이 느림(UNION SQL 필요)

Item item = em.find(Item.class, movie.getId());
System.out.println("item = " + item);

모든 테이블들을 다 찾는다.

Last updated