본문 바로가기

JPA

JPA 연관관계 매핑 3. 상속관계 매핑

상속관계 매핑

관계형 데이터 베이스에서는 상속관계가 존재하지 않는다.
슈퍼 타입 서브 타입 관계라는 모델링 기법으로 객체 상속과 유사하게 구현이 가능하다.

상속관계 매핑이란 객체의 상속 구조와 관계형 데이터베이스의 슈퍼 타입 서브타입 관계를 매핑해준다.

각각 테이블로 변환

  • 조인 전략 사용

통합 테이블로 변환

  • 단일 테이블 전략 사용

서브타입 테이블로 변환

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

1. 조인 전략

  • 테이블 생성 쿼리를 보면 Book table에서 item_id를 외래 키로 가지고 있는 것을 알 수 있다.
  • @DiscriminatorColumn으로 서브 타입의 값을 알 수 있다.
  • 서브 타입인 Book에서 @DiscriminatorValue("BOOK")을 통해 DTYPE에 넣을 값을 정할 수 있다.
@Entity
@Inheritance(strategy = InheritanceType.JOINED)
@DiscriminatorColumn
public class Item{

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

    private String name;

}

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

    private String author;
}

 

Hibernate:

create table Book (
   author varchar(255),
    item_id bigint not null,
    primary key (item_id)
)
Hibernate:

create table Item (
   DTYPE varchar(31) not null,
    item_id bigint not null,
    name varchar(255),
    primary key (item_id)
)
Hibernate:

alter table Book
   add constraint FK282k6114lkwimf5inj4oeyvuy
   foreign key (item_id)
   references Item

 

장점

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

단점

  • 조회 시 조인을 사용해야 하므로 성능이 저하됨
  • 조회 쿼리가 복잡하고 데이터 저장 시 Insert SQL 두 번 호출

2. 단일 테이블 전략

  • Ingeritance를 SINGLE_TABLE로 바꿔주면 된다.
  • 기본값이므로 Inheritance를 설정하지 않았다면 이 단일 테이블 전략으로 구동된다.
  • 테이블 생성 쿼리를 보면 만 그대로 Item 테이블에 모든 칼럼을 넣어버린다.
  • 만약 Album이라는 객체가 Item을 상속받았다면 Album에 값을 넣을 땐 author는 NULL이 될 것이다.
@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn
public class Item{

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

    private String name;

}

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

    private String author;
}

 

create table Item (
   DTYPE varchar(31) not null,
    item_id bigint not null,
    name varchar(255),
    author varchar(255),
    primary key (item_id)
)

 

장점

  • 조인이 필요 없으므로 조회 성능이 빠름
  • 조회 쿼리가 단순하다

단점

  • 자식 엔티티 중 자기 칼럼이 아니면 NULL 값이 들어간다.
  • 단일 테이블에 모든 것을 저장하므로 테이블이 커지며, 상황에 따라 조회 성능이 더 안 나올 수 있음.

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

  • Book Table만 생성되고 그 테이블 안에 Item의 속성들이 들어가게 된다.
  • Item은 추상 클래스로 구현한다.
@Entity
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
@DiscriminatorColumn
public abstract class Item{

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

    private String name;

}


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

    private String author;
}

 

장점

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

단점

  • 여러 자식 테이블을 함께 조회할 때 성능이 느리다.(UNION SQL 필요하다)
  • 자식 테이블을 통합해서 쿼리 하기 어렵다.

이 방법은 성능면에서나 구조면에서나 추천하지 않는 방법이다.


@MappedSuperclass

  • 공통 매핑 정보가 필요할 때 사용하면 좋은 방법이다.
  • 상속 관계 매핑, 엔티티, 테이블도 아니다.
  • 즉 상속받은 자식에게 매핑 정보만 제공한다.
  • 그러므로 추상 클래스로 만드는 게 좋다.
@MappedSuperclass
public abstract class BaseTimeEntity {

    private LocalDateTime createTime;

    private LocalDateTime lastModifiedDate;

}

@Entity
public class Member extends BaseTimeEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "member_id")
    private Long id;
    private String name;
}

 

create table Member (
   member_id bigint generated by default as identity,
    createTime timestamp,
    lastModifiedDate timestamp,
    name varchar(255),
    primary key (member_id)
)

Member에서 BaseTimeEntity를 상속받고 앱을 구동시켜보면 멤버 테이블에 BaseTimeEntity의 값들이 추가된 것을 알 수 있다.

 

@Entity 클래스는 엔티티나 @MappedSuperclass로 지정한 클래스만 상속 가능하다.

 


참고도서