본문 바로가기

JPA/상속관계 매핑

상속관계 매핑

슈퍼타입 서브타입 논리 모델 구현 전략

 

슈퍼타입 서브타입 논리 모델 구현 전략

객체의 상속과 DB의 슈퍼타입 서브타입 객체에는 상속이라는 개념이 존재한다. 하지만 관계형 DB에는 상속이라는 개념이 존재하지 않는다. 대신 슈퍼타입, 서브타입 관계라는 상속과는 다르지만

code-mania.tistory.com

반드시 위 글을 먼저 읽어주세요!!!

슈퍼타입 서브타입 관계의 테이블 매핑

이번 글에서는 슈퍼타입 서브타입 논리모델을 구현한 물리모델의 테이블들을 엔티티로 매핑할 것이다.
저번에 알아봤듯이 구현전략에는 3개가 있다.(조인 전략, 단일테이블 전략, 테이블 당 클래스 전략)
어떤 전략을 사용하든 우리는 Entity는 다 똑같다.
우리는 그저 어떤 전략을 사용했는지 JPA에게 알려주기만 하면 나머지 작업은 JPA가 처리해준다.

아래 그림은 저번 시간에 살펴본 논리모델을 Entity로 구현한 것이다.

 

주의할 점은 Appliance(슈퍼타입의 엔티티)는 추상 클래스로 선언한다는 것이다!
이는 Appliance 자체가 단독적으로 사용되는 것을 막기 위해서이다.
가전제품 데이터는 Thermostat(냉난방장치), Kitchenware(주방기구), AcousticEquipment(음향기구) 중 하나의 타입을 가져야 한다.
그런데 Appliance(슈퍼타입의 엔티티)를 단독적으로 사용해서 데이터를 저장하면 이 타입을 가지지 못한다.
이런 상황을 근본적으로 막기 위해 Appliance를 추상클래스로 선언한다.

 

Appliance.java

package hellojpa;

import lombok.Data;

import javax.persistence.*;

@Data
@Entity
@Inheritance(strategy = InheritanceType.JOINED)
@DiscriminatorColumn(name = "DTYPE")
public abstract class Appliance {

    @Id @GeneratedValue
    private Long id;

    private String name;

    private int price;
}

 

Thermostat.java

package hellojpa;

import lombok.Data;
import lombok.ToString;

import javax.persistence.DiscriminatorValue;
import javax.persistence.Entity;

@Entity
@Data @ToString(callSuper = true)
@DiscriminatorValue("T")
public class Thermostat extends Appliance {

    // 냉방 여부
    private boolean cooling;

    // 난방 여부
    private boolean heating;
}

AcousticEquipment.java

package hellojpa;

import lombok.Data;
import lombok.ToString;

import javax.persistence.DiscriminatorValue;
import javax.persistence.Entity;

@Entity
@Data @ToString(callSuper = true)
@DiscriminatorValue("A")
public class AcousticEquipment extends Appliance {
    // 무선여부
    private boolean wireless;
}

Kitchenware.java

package hellojpa;

import lombok.Data;
import lombok.ToString;

import javax.persistence.DiscriminatorValue;
import javax.persistence.Entity;

@Entity
@Data @ToString(callSuper = true)
@DiscriminatorValue("B")
public class Kitchenware extends Appliance { //주방용품

    // 제조사
    private String manufacturer;

    // 용도
    private String purpose;
}

Entity는 어떻게 설계되는지 살펴봤으니, 실제 설정은 어떻게 하는지 살펴보자!

딱 3개의 Annotation만 살펴보면 된다!!!

@Inheritance

슈퍼타입의 엔티티에 사용하는 어노테이션으로
논리 모델을 어떤 전략으로 구현했는지 JPA에게 알려주는 역할을 한다.

(@Inheritance는 Appliance.java에서 확인할 수 있다.
이 어노테이션 덕분에 전략을 바꿔도 코드는 거의 하나도 고치지 않아도 된다.
MyBatis 혹은 IBatis 같은 Framework를 사용하면, 이런 설계가 바뀔 때마다 끔찍한 일이 일어나게 될 것이다.)

기본값은 SINGLE_TABLE(단일 테이블 전략)이다.

  • strategy 옵션을 통해 전략 선택
    • InheritanceType.JOINED: JOIN 전략
    • InheritanceType.SINGLE_TABLE: 단일 테이블 전략
    • InheritanceType.TABLE_PER_CLASS: 구현 클래스 당 테이블 전략

@DiscriminatorColumn

슈퍼타입의 엔티티에 사용하는 어노테이션으로
타입의 판별을 위한 컬럼의 이름을 정해주는 어노테이션이다.

  • DB마다 구현되는 방식에는 조금씩 차이가 있음
  • name 옵션을 통해 타입 판별을 위한 컬럼의 이름을 설정할 수 있고,
  • 기본값은 `DTYPE`이며 관례로 `DTYPE`을 많이 사용한다.
  • 해당 옵션으로 생긴 컬럼에 값은 기본적으로 entity명이 들어간다.
    ex) Kitchenware Entity 저장 시 `DTYPE`에는 Kitchenware가 들어간다.
전략 별 차이

조인 전략
조인 전략은 타입판별 컬럼이 없어도 조인을 통해 타입 판별이 가능
따라서 @DiscriminatorColumn 어노테이션의 사용여부에 따라 타입판별 컬럼의 사용여부도 결정된다.

단일테이블 전략
단일테이블 전략은 타입판별 컬럼이 없으면 타입판별이 불가능
따라서 @DiscriminatorColumn 어노테이션의 사용여부에 관계없이 타입판별 컬럼이 사용된다.

테이블 당 클래스 전략
테이블 당 클래스 전략은 타입판별 컬럼이 필요가 없다.
따라서 @DiscriminatorColumn 어노테이션의 사용여부에 관계없이 타입판별 컬럼이 사용되지 않는다.

@DiscriminatorValue

서브타입의 엔티티에 설정하는 어노테이션으로
타입판별 컬럼에 해당 엔티티가 저장될 값을 정해준다.

  • Entity 저장 시 DTYPE에 들어가는 기본값은 해당 Entity의 이름이다.
  • value 옵션을 통해 엔티티의 구분값을 정해줄 수 있다.
    ex) @DiscriminatorValue(value = "M")
    `value =`은 생략 가능하다.
    ex) @DiscriminatorValue(value = "M")

더 구체적인 코드와 사용예시는 다음 시간에 알아보겠다!!!

 

참고강의: 배달의 민족 개발팀장 김영한 강사님의 JPA 강의

'JPA > 상속관계 매핑' 카테고리의 다른 글

상속관계 매핑 코드 실습  (0) 2021.08.18
슈퍼타입 서브타입 논리 모델 구현 전략  (0) 2021.08.16