본문 바로가기
Back-End/🌱 Spring Boot (java)

[Spring JPA] 상속관계 엔티티 매핑 전략 (@Inheritance @DiscriminatorColumn)

by 코딩하는 동현😎 2023. 2. 1.

이전 포스트들 보고 이어서 하기


상속관계 매핑

자바 객체는 상속 관계가 존재하지만, 관계형 데이터베이스는 상속관계가 대부분 없습니다.

 

JPA를 이용해서 매핑하는 법에는 세가지가 있는데 아래와 같습니다.

  • @Inheritance(strategy=InheritanceType.XXX)의 stategy를 설정해주면 됩니다.
    • default 전략은 SINGLE_TABLE(단일 테이블 전략)이다.
    • InheritanceType 종류
      1. JOINED
      2. SINGLE_TABLE
      3. TABLE_PER_CLASS
  • @DiscriminatorColumn(name="DTYPE")
    • 부모 클래스에 선언합니다. 자식 클래스를 구분하는 용도의 컬럼이다. 관례는 default = DTYPE
  • @DiscriminatorValue("XXX")
    • 자식 클래스에 선언한다. 엔티티를 저장할 때 슈퍼타입의 구분 컬럼에 저장할 값을 지정합니다.
    • 어노테이션을 선언하지 않을 경우 기본값으로 클래스 이름이 들어갑니다.

자바 엔티티 상속 객체 구현

우선 Item이라는 부모 클래스를 만든후, 아래 사진과 같이 Album과 Movie, Book 각 자식 클래스를 만들어 주겠습니다.

 

  • Item
package jpabook.jpastore.domain.item;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.*;
import lombok.*;

@Getter @Setter
@Entity
@Inheritance(strategy = InheritanceType.XXX) // 상속 구현 전략 선택
@DiscriminatorColumn(name = "dtype")
public class Item {
    @Id @GeneratedValue
    @Column(name = "item_id")
    private Long id;

    private String name;

    private int price;
    
    
    // 만약에 연관관계가 있다면 이렇게 부모클래스에다가 지정해주면 됩니다.
    //@ManyToMany(mappedBy = "items")
    //List<Category> categories = new ArrayList<>();

}

 

 

  • Album
package jpabook.jpastore.domain.item;
import javax.persistence.DiscriminatorValue;
import javax.persistence.Entity;
import lombok.Getter;
import lombok.Setter;

@Entity
@Getter
@Setter
@DiscriminatorValue("A")
public class Album extends Item {
    private String artist;
    private String etc;
}

 

 

  • Book
package jpabook.jpastore.domain.item;
import javax.persistence.DiscriminatorValue;
import javax.persistence.Entity;
import lombok.Getter;
import lombok.Setter;

@Entity
@Getter
@Setter
@DiscriminatorValue("B") // 구분짓는 칼럼(속성)값 dtype = B
public class Book extends Item {
    private String author;
    private String isbn;
}

 

  • Movie
package jpabook.jpastore.domain.item;
import javax.persistence.DiscriminatorValue;
import javax.persistence.Entity;
import lombok.Getter;
import lombok.Setter;

@Entity
@Getter
@Setter
@DiscriminatorValue("M") // 구분짓는 칼럼(속성)값 dtype = M
public class Movie extends Item {
    private String director;
    private String actor;
}

이렇게 엔티티를 만들어주면 되지만, 세가지 전략중 골라서 적용을 해야됩니다.

그 중 실무에서 많이 쓰이는 두가지 상속관계 매핑 전략을 소개하겠습니다. (TABLE_PER_CLASS는 실무에서 안쓰입니다.)

 

 

1. 개별적인 테이블로 변환하고 조회할때마다 조인하는 조인 전략(JOINED)

부모엔티티에 @Inheritance(strategy = InheritanceType.JOINED) 와 같이 작성해서 정의해 줄 수 있습니다.

개별적인 테이블 각각 만든 다음에 필요할때마다 조인해서 조회하는 전략입니다.

테이블이 정규화되어서 공간낭비가 없는게 큰 장점입니다.

그러나 조회할때마다 조인을 해서 조회를 해야하기 때문에 뒤에 나오는 단일 테이블 전략 보다는 성능이 안좋습니다.

package jpabook.jpastore.domain.item;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.*;
import lombok.*;

@Getter @Setter
@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;
    
    
    // 만약에 연관관계가 있다면 이렇게 부모클래스에다가 지정해주면 됩니다.
    //@ManyToMany(mappedBy = "items")
    //List<Category> categories = new ArrayList<>();

}

2. 통합 테이블로 변환하는 단일 테이블 전략 (SINGLE_TABLE)

부모 엔티티에 @Inheritance(strategy = InheritanceType.SINGLE_TABLE) 어노테이션을 이용해서 적용해줄수 있습니다.

보면 아시겠지만 모든 속성을 한 테이블에 몰아놨기 때문에 몇 칼럼들이 항상 공간이 낭비될수 밖에 없습니다.

서비스 규모가 크지 않고 공간 낭비가 크게 다가오지 않을때 쓸수 있으나, 조인할 필요가 없으므로 데이터 추가 쿼리도 한번 조회 쿼리 한번이어서 성능이 좋습니다.

package jpabook.jpastore.domain.item;

import java.util.ArrayList;
import java.util.List;

import javax.persistence.*;

import lombok.*;

@Getter @Setter
@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;

    //  Item -> OrderItem 방향으로 가지 않기 때문에 List<OrderItem> 안함


    @ManyToMany(mappedBy = "items")
    List<Category> categories = new ArrayList<>();

}

단일 테이블 전략 같은 경우에는 다음과 같은 SQL쿼리가 적용됩니다.

    create table item (
       dtype varchar(31) not null,
        item_id bigint not null,
        name varchar(255),
        price integer not null,
        artist varchar(255),
        etc varchar(255),
        author varchar(255),
        isbn varchar(255),
        actor varchar(255),
        director varchar(255),
        primary key (item_id)
    ) engine=InnoDB
반응형

댓글