JPA

[JPA] 상속관계 매핑 전략이란? @Inheritance, @DiscriminatorColumn이란?

Jeong Jeon
반응형

상속관계 매핑이란?

객체는 상속관계가 존재하지만 관계형 데이터베이스에는 존재하지 않는 개념이다.

객체의 상속관계를 관계형 데이터베이스의 슈퍼타입/서브타입 관계를 매핑하는것..!

그렇다면 논리 모델을 물리 모델로 어떻게 구현할 수 있을까?

JPA 입장에서는 간단하게 매핑전략만 선택하면 DB입장에서 봤을때 3가지 방식으로 구현할 수 있다.

 

  • @Inheritance(strategy=InheritanceType.전략)
    -> default 전략은 SINGLE_TABLE(단일테이블 전략)이다.
    -> 전략종류 :

    1). SINGLE_TABLE : 단일테이블에 한번에 모든 정보를 다 담는 전략.
        위의 사진에 따르면 ALBUM / MOVIE / BOOK 별도의 테이블에 데이터를 저장한다.
    2). JOINED : 각각의 테이블별로 정규화된 방법.
    3). TABLE_PER_CLASS : 엔티티 별로 각 테이블별로 분리하여 담는다.
  • @DiscriminatorColumn(name="DTYPE")
    -> 부모클래스에 선언하며, 하위클래스를 구분하는 용도의 컬럼을 만든다. default = DTYPE
  • @DiscriminatorValue("밸류")
    -> 하위 클래스에 선언하며, 슈퍼타입의 구분컬럼에 저장할 값을 지정하여 사용한다.
    -> 어노테이션을 사용하지 않으면 기본값으로 클래스이름이 설정된다.

 

한번 각각의 전략을 어떻게 사용하는지 코드로 알아보자.

 

공통된 자식 Entity를 먼저 구현하고 보자.

[자식-1]
@Entity
public class Album extends Item {

   private String artist;
}

[자식-2]
@Entity
public class Movie extends Item {

   private String director;
   private String actor;
}

[자식-3]
@Entity
public class Book extends Item {

   private String author;
   private String isbn;
}

 

 

[SINGLE_TABLE 전략]

한테이블에 다 때려넣는 전략 -> 정규화를 안해서 빠르긴 하지만 불필요한 공간도 많이 사용할 수 있다. 상황에 맞게 쓰자. 테이블에 모든 컬럼을 저장하기 때문에, DTYPE 없이는 테이블을 판단할 수 없음을 유의하자.

 

[부모]
@Entity
@DiscriminatorColumn
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
public class Item {

   @Id
   @GeneratedValue(strategy = GenerationType.IDENTITY)
   private Long id;

   private String name;
   private int price;
}

쿼리를 확인해 보자

Hibernate:
   create table Item (
      DTYPE varchar(31) not null,
      id bigint generated by default as identity,
      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)
  )

 

장단점을 확인해보자

  • 장점
    • 조인이 필요 없으므로 일반적인 조회 성능이 빠르다.
    • 조회 쿼리가 단순핟.
  • 단점
    • 자식 엔티티가 매핑한 컬럼은 모두 NULL을 허용해야 한다.
    • 단일 테이블에 모든 것을 저장하므로 테이블이 커질 수 있다.
    • 상황에 따라서 조인 전략보다 성능이 오히려 느려질 수 있다.

 

 

[JOINED 전략]

-> 각각 테이블로 만들어진다.

[부모]
@Entity
@Inheritance(strategy = InheritanceType.전략) 
@DiscriminatorColumn
public class Item {

   @Id
   @GeneratedValue(strategy = GenerationType.IDENTITY)
   private Long id;

   private String name;
   private int price;
}

 

실행 쿼리를 한번 보자!

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 (
      DTYPE varchar(31) not null,
      id bigint generated by default as identity,
      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)
  )
   
   
Hibernate:
   alter table Album
      add constraint FKcwer2ph22we3ye8rbk26h5jm9
      foreign key (id)
      references Item
Hibernate:
   alter table Book
      add constraint FK32ksjioef2w80wjw3ijr282ji
      foreign key (id)
      references Item
Hibernate:
   alter table Movie
      add constraint FK792jkwehjrkwe792931hjew6s
      foreign key (id)
      references Item

테이블이 하나씩 생성되는것을 볼수있다.

 

장단점도 확인해보자.

  • 장점
    • 테이블이 정규화가 잘되어있다.
    • 외래 키 참조 무결성 제약조건 활용 가능
      • ITEM의 PK가 ALBUM, MOVIE, BOOK의 PK이자 FK이다. 그래서 다른 테이블에서 아이템 테이블만 바라보도록 설계하는 것이 가능 하다
  • 단점
    • 조회시 조인을 많이 사용한다. 단일 테이블 전략에 비하면 성능이 안나온다. 조인하니까.
    • 데이터 저장시에 INSERT 쿼리가 상위, 하위 테이블 두번 발생한다.
  • 정리
    • 오히려 저장공간이 효율화 되기 때문에 장점이 크다.

 

[TABLE_PER_CLASS 전략]

Join전략과 비슷하지만 부모Entity의 컬럼들을 자식엔티티로 넘겨버린다.

즉 name,price 컬럼들이 중복되어 생성된다.

부모 Entity는 실제로 생성되는 테이블이 아니므로, 무조건 abstract 클래스여야하고, @DiscriminatorColumn도 필요없어진다.

 

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

   @Id
   @GeneratedValue(strategy = GenerationType.AUTO)
   private Long id;

   private String name;
   private int price;
}

 

쿼리를 확인해보자

Hibernate:
   create table Album (
      id bigint not null,
       name varchar(255),
       price integer not null,
       artist varchar(255),
       primary key (id)
  )
Hibernate:
   create table Book (
      id bigint not null,
       name varchar(255),
       price integer not null,
       author varchar(255),
       isbn varchar(255),
       primary key (id)
  )
Hibernate:
   create table Movie (
      id bigint not null,
       name varchar(255),
       price integer not null,
       actor varchar(255),
       director varchar(255),
       primary key (id)
  )

세개의 테이블만 생성되는것을 확인할 수 있다.

  • 장점
    • 서브 타입을 명확하게 구분해서 처리할 때 효과적이다
    • NOT NULL 제약조건을 사용할 수 있다.
  • 단점
    • 여러 자식 테이블을 함께 조회할 때 성능이 느리다(UNION SQL)
    • 자식 테이블을 통합해서 쿼리하기 어렵다.
    • 변경이라는 관점으로 접근할 때 굉장히 좋지 않다.

잘사용하지 않는 전략이라고 보면 될것같다.

 

 

반응형