[JPA] 5.상속관계 매핑
Updated:
상속관계 매핑
객체는 상속관계를 이용하여 모델링하면 되지만 DB는 상속관계가 존재하지 않는다는 차이점이 존재한다. 여기서 DB는 어떻게 상속관계를 처리할 것인지에 대한 고민이 필요하다. 이 게시글에서는 여러가지 상속관계 매팽 기법들과 사용하는 어노테이션들에 대해 알아본다
배경지식
RDB는 상속관계가 존재하지 않으므로 그나마 유사한 슈퍼타입, 서브타입 관계라는 모델링 기법과 객체의 상속을 매핑한다

아래부터는 이 예시를 이용하여 설명을 하고자 한다
상속관계 매핑 종류
- 조인 전략: 각각 테이블로 변환
- 단일 테이블 전략: 통합 테이블로 변환
- 구현 클래스마다 테이블 전략: 서브타입 테이블로 변환
주요 어노테이션
@Inheritance- 상속관계 매핑을 수행한다고 명시한다
strategy속성을 통해 조인 전략, 단일 테이블 전략, 구현클래스마다 테이블 전략 3가지로 구분한다
@DiscriminatorColumn- 어떤 컬럼을 테이블 구분값으로 사용할지 명시한다
@DiscriminatorValue- 각 테이블을 구분하기 위해 테이블별로
@DiscriminatorColumn로 어떤 값을 설정할지 명시한다
- 각 테이블을 구분하기 위해 테이블별로
1. 상속관계 매핑의 전략들
1-1. 조인 전략
주로 가장 많이 사용하는 전략이다

- 객체의 상속관계와 유사한 모양으로 테이블을 모델링한다
- 슈퍼타입(ITEM)의
DTYPE컬럼으로 서브타입(ALBUM, MOVIE, BOOK)을 구분한다- 사실 조인 전략은 각각의 서브타입들의 테이블이 분리되어 존재하기 때문에
DTYPE이 없어도 된다
- 사실 조인 전략은 각각의 서브타입들의 테이블이 분리되어 존재하기 때문에
- 각각의 서브타입은 슈퍼타입의 PK를 자신의 PK 및 FK로 가진다
- 테이블 정규화 측면에서 유용한 전략이지만 조회 시에 조인을 많이 사용하고, 저장시에는 INSERT문을 슈퍼타입, 서브타입 총 2개를 호출해야 한다는 단점이 존재한다
이제 코드를 통해 살펴보자
@Entity
@Inheritance(strategy = InheritanceType.JOINED)
@DiscriminatorColumn(name = "dtype")
@Getter @Setter
public abstract class Item {
@Id @GeneratedValue
private Long id;
private String name;
private int price;
}
@Entity
@DiscriminatorValue(value = "A")
@Getter @Setter
public class Album extends Item{
private String artist;
}
@Entity
@DiscriminatorValue(value = "B")
@Getter @Setter
public class Book extends Item{
private String author;
private String isbn;
}
@Entity
@DiscriminatorValue(value = "M")
@Getter @Setter
public class Movie extends Item{
private String director;
private String actor;
}
@Inheritance의strategy속성을 통해 조인 전략을 사용함을 명시한다@DiscriminatorColumn를 통해 서브타입 테이블의 구분을 위해dtype을 사용함을 명시한다@DiscriminatorValue를 통해 각각의 서브타입 테이블이value속성에 명시한 값을dtype으로 가짐을 명시한다
1-2. 단일 테이블 전략
프로젝트가 복잡하지 않고 변경될 여지가 거의 없는 경우에 사용하는 전략이다

- 몇개의 서브타입이 존재하든 하나의 테이블만을 만들고 dtype 컬럼을 통해 구분한다
- 조인이 필요없어 조회 성능이 우수하고 조회 쿼리 또한 단순하다
- 다른 서브타입들의 컬럼 모두가 들어있기 때문에 null을 허용하고 테이블이 거대해지는 문제가 발생한다
@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "dtype")
@Getter @Setter
public abstract class Item {
@Id @GeneratedValue
private Long id;
private String name;
private int price;
}
- 조인 전략의 코드에서
@Inheritance의strategy속성만InheritanceType.SINGLE_TABLE로 변경해주면 된다 - DB를 살펴보면 조인 전략과 다르게 ALBUM, BOOK, MOVIE 테이블이 생성되지 않고 ITEM 테이블에 모든 컬럼들이 들어있음을 확인할 수 있다
- 단, 다른 서브타입의 컬럼들은 null값으로 들어간다
1-3. 구현 클래스마다 테이블 전략
DB 설계자와 ORM 전문가 모두 추천하지 않는 전략으로 사용하지 말 것!!!

- 조인 전략과 비슷하지만 슈퍼타입(ITEM)을 없애고 서브타입(ALBUM, MOVIE, BOOK)에 슈퍼타입의 컬럼들을 모두 집어넣은 방식이다
- 실제로 DB에 슈퍼타입(ITEM)이 생성되지 않고 서브타입(ALBUM, MOVIE, BOOK)만 생성된다
- 데이터 저장 시에는 편리하지만 조회 시에 UNION SQL이 필요하여 성능이 느리다는 단점을 가진다
@Entity
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
@Getter @Setter
public abstract class Item {
@Id @GeneratedValue
private Long id;
private String name;
private int price;
}
- 서브타입에서 각각의 테이블이 존재하기 때문에
@DiscriminatorColumn가 필요없다
2. @MappedSuperclass
상속관계 매핑과 관계없이 여러 객체가 동일한 속성을 사용할때 중복코드를 제거하기 위해 사용하는 어노테이션이다

- 객체들 간의 공통 속성은 슈퍼타입으로 따로 만들어 생성하고 서브타입은 각자만의 속성을 가지고 슈퍼타입을 상속받도록 설계한다
- 상속관계 매핑이 아니며 엔티티도 아님을 명심하고 직접 생성하여 사용할 일이 없으므로 추상 클래스가 권장된다
- 단지, 여러 엔티티가 공통으로 사용하는 필드를 모으는 역할을 한다
- 주로 등록일, 수정일, 등록자, 수정자 같이 전체 엔티티에서 공통으로 사용되는 코드의 중복성을 제거하기 위해 사용한다
@MappedSuperclass
@Getter @Setter
public abstract class BaseEntity {
private String createdBy;
private String updatedBy;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
}
- 위와 같이 공통 속성을 모아놓고
@MappedSuperclass를 붙여 추상클래스로 만든다 - 위 필드가 필요한 엔티티는
extends키워드로 상속받아 사용한다
Leave a comment