본문 바로가기
ORM(JPA)

[JPA] 상속 관계 매핑

by hjhello423 2020. 2. 3.

객체의 상속 관계를 DB에 어떻게 매핑하는지 알아보겠습니다.

우선 객체에 존재하는 상속관계가 DB에서는 표현할 수 없습니다.

비슷하게 Super-Type Sub-Type 관계라는 모델링 기법이 상속과 유사한 모습을 하고 있습니다.

슈퍼 서브타입 관계를 모델링할 때는 3가지의 방법이 있습니다.

  • 각각의 테이블로 변환
  • 통합 테이블로 변환
  • 서브타입 테이블로 변환

아래 그림과 같이 물품에 속하는 음반, 영화, 책 모델이 있다고 생각하고 위의 3가지 방법을 차래대로 살펴보겠습니다.


Joined Strategy (조인 전략)

위 그림의 음반, 영화, 책 모델을 각각의 자식 테이블로 만들고 key를 기본키+부모의 외래 키로 사용하는 전략입니다.

테이블이 정규화되고 FK 참조 무결성 제약조건 활용, 저장공간 효율성장점이 있습니다.

반대로 조회할 때 join을 자주 사용하는 성능 저하, 조회 쿼리의 복잡성, 데이터를 등록할 때 insert sql을 2번 실행하는 단점이 있습니다.

@Entity
@Inheritance(strategy = InheritanceType.JOINED)
@DiscriminatorColumn(name = "dtype")
public class Item {

    @Id
    @GeneratedValue
    @Column(name = "item_id")
    private Long id;

    private String name;

    private int price;
}

 

@Entity
@DiscriminatorValue("A")
public class Album extends Item {

    private String artist;
}

 

@Entity
@DiscriminatorValue("M")
public class Movie extends Item {

    private String director;
    private String actor;
}

 

@Entity
@DiscriminatorValue("B")
@PrimaryKeyJoinColumn(name = "book_id")
public class Book extends Item {

    private String author;
    private String isbn;
}

ERD와 코드를 함께 보고 하나씩 알아보겠습니다.

먼저 Item@Inheritance(strategy = InheritanceType.JOINED)은 부모 class에 어노테이션을 정의하며, 조인 전략 사용을 선언하는 의미입니다.

@DiscriminatorColumn(name = "dtype")은 부모 클래스에 자식의 구분 컬럼을 지정합니다. 기본값이 dtype이므로 생략 가능합니다.

@DiscriminatorValue("A")는 엔티티를 저장할 때 구분 컬럽(dtype)에 입력할 값을 지정합니다. 여기선 A가 저장되겠죠?

@PrimaryKeyJoinColumn(name = "book_id")은 부모 테이블의 키 컬럼명을 변경할 때 쓰입니다. 자식 테이블은 부모의 id 컬럼명을 그대로 사용하는데 여기서는 BOOK 테이블의 ITEM_ID 컬럼 명을 BOOK_ID로 변경합니다.



 



Single-Table Strategy (단일 테이블 전략)

테이블 1개를 사용해 데이터를 저장하는 전략입니다.

자식 엔티티가 매핑한 컬럼은 모두 null을 허용해야 합니다.

조회 성능이 빠르고 조회 쿼리가 단순장점이 있습니다.

자식 엔티티가 매핑한 컬럼은 모두 null을 허용해야 하며, 운영 중 테이블이 계속해서 커지고 이로 인해 조회 성능이 느려질 수 있는 단점이 있습니다.

@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "dtype")
public class Item {

    @Id
    @GeneratedValue
    @Column(name = "item_id")
    private Long id;

    private String name;

    private int price;
}

@Inheritance(strategy = InheritanceType.SINGLE_TABLE)은 싱글 테이블 전략을 사용하겠다는 의미입니다.

나머지 Album, Book, Movie는 이전과 동일합니다.


구현 클래스 마다 테이블 전략

자식 엔티티마다 테이블을 생성하는 전략 입니다. 추천하지 않는 전략 입니다.

서브 타입을 구분해서 처리 할때 효과적이며, not null 제약조건 사용이 가능한 장점이 있습니다.

여러 자식 테이블을 조회할때 UNION을 사용하여 성능이 저하되며, 자식 테이블을 통합해서 쿼리하기 어려운 단점이 있습니다.

 

 

@Entity
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
@DiscriminatorColumn(name = "dtype")
public class Item {

    @Id
    @GeneratedValue
    @Column(name = "item_id")
    private Long id;

    private String name;

    private int price;
}

@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)은 구현클래스-테이블 전략 사용을 의미합니다.

다른 모델의 소스는 위와 동일합니다.


참고

자바 ORM 표준 JPA 프로그래밍, 김영한, 에이콘

반응형

댓글