JPA 상속관계 매핑

진기·2024년 4월 1일

상속 관계에서의 매핑

  • 관계형 데이터베이스는 상속 관계가 없다.
  • 슈퍼타입, 서브타입 관계라는 모델링 기법이 객체 상속과 유사하다고 볼 수 있다.
  • 상속관계 매핑은 객체의 상속구조 DB의 슈퍼타입, 서브타입 관계를 매핑한다.
  • 크게는 조인 전략, 단일테이블 전략, 구현 클래스마다 테이블 전략으로 볼 수 있다.

슈퍼타입, 서브타입?

  • 슈퍼타입 : 공통의 부분 (서브타입을 갖는 엔티티)
  • 서브타입: 엔티티와 같은 데이터의 집합 (엔티티를 상세화하거나 통합화하는 과정에서 주로 도출)

슈퍼타입, 서브타입 논리 모델을 실제 물리 모델로 구현하는 방법

  • 각각 테이블로 변환 -> 조인 전략
  • 통합 테이블로 변환 -> 단일 테이블 전략
  • 서브타입 테이블로 변환 -> 구현 클래스마다 테이블 전략

아래와 같이 Item을 상속받게 된채로 아무것도 설정을 하지 않고 실행을 하게 되면,

@Entity
public abstract Item {

    @Id @GeneratedValue
    private Long id;

    private String name;
    private int price;
}

@Entity
public class Book extends Item {

    private String author;
    private String isbn;
}

@Entity
public class Album extends Item {

    private String artist;
}

@Entity
public class Movie extends Item {

    private String director;
    private String actor;
}

아래와 같이 단일 테이블(Item)에서 모든 칼럼들이 생성되는 것을 확인할 수 있다.

create table item (
       dtype varchar(31) not null,
        id bigint not null,
        name varchar(255),
        price integer not null,
        artist varchar(255),
        author varchar(255),
        isbn varchar(255),
        actor varchar(255),
        director varchar(255),
        primary key (id)
    )

이 이유는 기본적으로 Inheritance가 SINGLE_TABLE로 설정되어있기 때문이다.


조인 전략

  • 조인 전략을 정석이라고 생각하고 테이블을 구성해야된다.

장점)

  • 테이블 정규화
  • 외래 키 참조 무결성 제약조건으로 활용 가능
  • 저장공간의 효율화

단점)

  • 조회시 조인을 많이 사용하여 성능 저하
  • 조회 쿼리가 복잡함
  • 데이터 저장시 INSERT SQL 2번 호출함

@Entity
@Inheritance(strategy = InheritanceType.JOINED)
public class Item {

    @Id @GeneratedValue
    private Long id;

    private String name;
    private int price;
}

2번째 라인을 추가해줘야한다. 설정을 JOINED로 해주게 되면 아래와 같은 결과를 얻는다.

Hibernate: 
    
    create table album (
       artist varchar(255),
        id bigint not null,
        primary key (id)
    )
Hibernate: 
    
    create table book (
       author varchar(255),
        isbn varchar(255),
        id bigint not null,
        primary key (id)
    )
Hibernate: 
    
    create table item (
       id bigint not null,
        name varchar(255),
        price integer not null,
        primary key (id)
    )
Hibernate: 
    
    create table movie (
       actor varchar(255),
        director varchar(255),
        id bigint not null,
        primary key (id)
    )

각 테이블의 ID는 Item 테이블의 ID라는 것을 알 수 있다.


단일 테이블 전략

장점)

  • 조인이 필요 없으므로 일반적으로 조회 성능이 빠름
  • 조회 쿼리가 단순함

단점)

  • 자식 엔티티가 매핑한 칼럼은 모두 Null허용
    • null 허용 한다는 것이 만약 ALBUM 관련 데이터를 넣어줄 때에는 MOVIE 나 BOOK 관련 데이터는 모두 Null을 허용해줘야한다.
  • 단일 테이블에 모든 것을 저장하므로 테이블이 커질 수 있으며, 상황에 따라서 조회 성능이 느려질 수 있다.

단일 테이블을 보면 DTYPE 칼럼이 생긴것을 알 수 있다.
DTYPE은 어떤 테이블에 관련되서 ITEM이 사용하고 있는지에 대해 구체적으로 알 수 있게 해준다.

@Entity
@Inheritance(strategy = InheritanceType.JOINED)
@DiscriminatorColumn
@Getter @Setter
public class Item {

    @Id @GeneratedValue
    private Long id;

    private String name;
    private int price;
}

DTYPE을 추가하고 싶을 때에는 3번째 라인을 추가하면 된다.
DTYPE의 이름을 따로 저장하고 싶을 때에는 ""안에 원하는 이름을 지정하게 되면 그에 따라 생성되게 된다.
DTYPE은 조인 전략에서는 어떻게든 관련 테이블을 찾을 수 있지만, 단일 테이블 전략에서는 찾기 힘들어 꼭 들어가야 된다.

@DiscriminatorColumn(name = "")

그리고 DTYPE 안에 각 테이블 관련된 이름을 지정하고 싶으면

@Entity
@DiscriminatorValue("Book")
public class Book extends Item{

    private String author;
    private String isbn;
}

2번째 라인처럼 이름을 따로 지정해주면 된다.

그리고 @Inheritance를 사용하면서 단일 테이블로 수정하고 싶을 때에는 JOINED -> SINGLE_TABLE로 변경하면 된다.

@Inheritance(strategy = InheritanceType.SINGLE_TABLE)

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

이 전략은 데이터베이스 설계자와 ORM 전문가 둘 다 추천하지 않는다.

장점)

  • 서브 타입을 명확하게 구분해서 처리할 때 효과적
  • not null 제약 조건 사용 가능

단점)

  • 여러 자식 테이블을 함께 조회할 때 성능이 느림
    • Item ID로 조회할 경우 어느 테이블에 들어가있는지 알 수 없으므로 union을 사용해서 ALBUM, MOVIE, BOOK을 모두 조회하게 된다.
  • 자식 테이블을 통합해서 쿼리하기 어려움

아래와 같이 구현 클래스마다 테이블 전략으로 실행하고 싶으면 TABLE_PER_CLASS 로 변경

@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
profile
개발 성장 이야기

0개의 댓글