JPA - 상속관계 맵핑

YeongUng Kim·2021년 6월 13일
0

JPA

목록 보기
2/4
post-thumbnail

오브젝트의 상속관계를 RDB의 테이블로 표현하는 JPA의 기능이다. 엄밀하게 말하자면 상속관계가 존재하지 않는 RDB와 상속관계가 존재하는 자바코드를 서로 맵핑하는 것을 목적으로 한다.

상속관계 맵핑 개요

  • 자바와 같은 객체지향 언어에는 상속(Inheritance)이 존재한다
  • 관계형 데이터베이스(RDB)에서는 기본적으로 상속관계의 개념은 없다
  • 자바 코드단의 엔티티 상속을 RDB 테이블에 적용하는게 오늘의 목적 되시겠다

논리모델 - 물리모델 구현

논리모델의 상속관계는 오브젝트 모델로도 표현이 가능하다.하지만 RDB에서는 상속이 불가능 하므로 논리모델 에서의 슈퍼타입 - 서브타입을 구현할 방법이 필요하다.


자바 코드단은 위의 논리모델을 구현하기 용이하다 ITEM class를 구현하고 각 자식클래스에서 상속해주면 끝난다. 하지만 RDB에서는 위와같은 방법이 불가능하기 때문에 전략을 설정해줘야한다.

전략

  • @Inheritance(strategy=InheritanceType.XXXX)
  • @DiscriminatorColumn(name="DTYPE")
  • @DiscriminatorValue("NAME")

@Inheritance(strategy=InheritanceType.XXX)

Inheritance 어노테이션의 전략은 크게 3가지다.

  • JOINED
  • SINGLE_TABLE
  • TABLE_PER_CLASS

default 전략은 SINGLE_TABLE 이다.


Item

@Entity
@Data
// 전략 설정 어노테이션
@Inheritance(strategy = InheritanceType.XXXX)
public class Item {
    @Id @GeneratedValue
    private Long id;
    
    private String name;
    private int price;
}

BOOK

@Entity
@Data
public class Book extends Item{ // Item 상속
    private String author;
    private String isbn;
}

Movie

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

슈퍼 클래스인 Item에 @Inheritance 어노테이션을 적용해주고 전략을 설정해준다. InheritanceType에 적용할 전략을 선언한다.각 서브클래스는 슈퍼클래스인 Item을 상속받아 사용한다.(getter setter 쓰기 귀찮아서 lombok @Data 어노테이션을 사용했다. 코틀린은 getter setter를 내부적으로 생성해준다. 확실히 자바보다 코틀린의 생산성이 높은 이유가 있다.)

그러면 이제 각 전략을 알아보자


SINGLE_TABLE

  • @Inheritance(strategy=InheritanceType.SINGLE_TABLE)
  • 단일 테이블 전략
  • 각 서브 클래스의 모든 필드를 한 테이블에 생성한다
  • 해당 서비스가 단순하고 굳이 조인전략이 필요 없다고 판단될때 쓰면된다.

장점

  • 굳이 조인을 해도 되지 않으므로 성능상의 이점이 있다.INSERT,SELECT 쿼리 한방이면 된다.
  • DTYPE 필드를 이용하여 서브 클래스를 특정할 수 있다.
  • 조회 쿼리도 단순하다 WHERE절에 DTYPE을 걸어놓고 검색할 수 있다

단점

  • 특정 자식 엔티티를 맵핑하면 나머지 자식 엔티티의 필드는 모두 null이다.
  • 단일 테이블에 모든 정보를 저장하므로 테이블의 크기가 커진다.
  • 테이블의 크기가 커질 경우 오히려 조회성능이 느려질 경우가 있다.
  • DTYPE이 없으면 서브 엔티티 판단이 불가능하다.

JOINED

  • @Inheritance(strategy=InheritanceType.JOINED)
  • 정규화된 방법
  • ITEM,BOOK,MOVIE,ALBUM 각각의 테이블이 생성되고 각 엔티티를 맵핑할때 해당 엔티티와 ITEM 엔티티가 맵핑되어 저장된다.
  • 서브 클래스에 대응되는 테이블은 ITEM의 id를 PK,FK로 지정한다.(당연..)
  • DTYPE은 클래스 이름으로 지정된다(디폴트)

장점

  • 테이블 정규화
  • 정규화 되어있다 보니 단일 테이블의 null처럼 쓸데없는 공간을 잡아먹지 않는다

단점

  • 아무래도 조인을 하다보니 성능상의 이점이 적다.
  • INSERT가 두번 나간다(ITEM하나, 서브 엔티티 하나)
  • 조회 쿼리가 복잡하다.

요즘은 DB성능이 워낙 좋아지고 단일 테이블에 비해 성능 차이도 체감할 정도는 아니다 보니 정규화 되어있는 JOINED를 많이 사용하라고 권고하는편.


TABLE_PER_CLASS

  • @Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
  • 각 서브 엔티티의 테이블에 Item 엔티티의 필드가 모두 포함 된다.
  • 테이블에서 슈퍼 클래스를 없애고 슈퍼클래스의 필드를 서브클래스에 각각 정의한다.
  • ITEM은 abstarct로 설정
  • 다시말하면, ITEM 테이블은 없고 BOOK,MOVIE,ALBUM만 있는것.

장점

  • NOT NULL 제약조건 사용가능
  • 서브 엔티티를 명확하게 구분해야 할경우 효과적이다.

단점

  • 자식 테이블을 조회할때 느리다.실제 쿼리는 UNION을 사용하기 때문
  • 자식 테이블을 통합하여 관리하기 어렵다.
  • 변경 관점에서 굉장히 유연하지 못하다.

설명을 덧붙이자면 TABLE_PER_CLASS로 전략을 설정하게 되고 조회했을시,

Item item = em.find(Item.class,movie.getId());

와 같은 코드로 조회시 모든 서브 엔티티를 호출하여 UNION하게 되므로 성능이 떨어질 수 밖에 없다.
결론 : TABLE_PER_CLASS는 웬만하면 쓰지말자


@MappedSuperClass

해당 어노테이션은 상속개념과는 관계가 없고, 엔티티들이 공통적인 속성을 가질때 공통필드를 BaseEnitiy로 따로 묶어서 관리해줄때 사용한다.
예를들면, 모든 테이블에는 생성시각,생성자,수정시각,수정자와 같은 모든 테이블 에서 공통적으로 포함되어야할 컬럼을 BaseEntitiy로 묶어주고 해당 어노테이션을 사용하여 관리하는것. 다시말하면 맵핑 정보만 상속받는다

@MappedSuperclass
public abstract class BaseEntity {
    private LocalDateTime createAt;
    private String createBy;
    private LocalDateTime lastModifiedAt;
    private String modifiedBy;
}
  • 각 엔티티에서 상속 받아 사용해주면 된다.
  • 보면 @Entity도 안붙었다. 그러다보니 조회 검색이 안된다.
  • 맵핑 정보만 받아서 사용하는 용도
  • 직접 사용하는 경우는 없으므로 추상 클래스로 사용하길 권장.

출처

  • 자바 ORM 표준 JPA 프로그래밍
profile
기록하지 않으면 까먹어서 만든 블로그..

0개의 댓글