고급매핑
상속관계 매핑
객체와 달리 관계형 데이터베이스는 상속 관계가 존재하지 않는다.
- 상속관계 대신 슈퍼타입 서브타입 관계라는 모델링 기법이 존재하며 이는 객체의 상속과 유사하다.
- ORM에서 상속관계 매핑은 객체와 상속 구조와 DB의 슈퍼타입 서브타입을 매핑하는 것이다.

슈퍼타입 서브타입 논리 모델과, 객체 상속 모델을 나타낸 그림이다.
슈퍼타입 서브타입 논리 모델을 실제 물리 모델로 구현하는 방법
- 각각 테이블로 변환 : 각각 테이블을 만들고 조회할 때 조인을 사용한다. -> 조인 전략
- 비즈니스적으로 중요하고, 복잡한 경우에 선택한다.
- 통합 테이블로 변환 : 테이블을 하나만 사용해서 통합한다.-> 단일 테이블 전략
- 서브타입 테이블로 변환 : 서브타입마다 하나의 테이블로 만든다. -> 구현 클래스마다 테이블 전략
주요 어노테이션
@Inheritance(strategy=InheritanceType.XXX)
- JOINED : 조인전략
- SINGLE_TABLE : 단일 테이블 전략
- TABLE_PER_CLASS : 구현 클래스마다 테이블 전략
@DiscriminatorColumn(name="DTYPE")
항상 작성하는 것을 추천한다.
name="DYTPE"에는 기본값으로 엔티티명이 들어가며, 이게 있는 것이 편하고 좋다.
@DiscriminatorValue("XXX")
엔티티 이름은 클래스 값과 동일(기본값)하지만 바꿀 수 있다.
조인전략

- 객체와 다르게 테이블은 타입이 없으므로 컬럼을 추가하며 이렇게 추가한 컬럼 DTYPE은 album인지 movie인지 구분할 컬럼이다.
- 엔티티마다 테이블로 만들어 준 뒤, 부모의 기본키를 기본키와 외래키로 사용한다.
- 그래서 조회할 때 조인을 사용한다.
@Entity
@Inheritance(strategy = InheritanceType.JOINED)
@DiscriminatorColumn(name = "DTYPE")
public abstract class Item{
@Id @GeneratedValue
@Column(name = "ITEM_ID")
private Long id;
private String name;
private int price;
}
@Entity
@DiscriminatorValue("A")
public class Album extends Item{
private String artist;
}
...
- @Inheritance(strategy = InheritanceType.JOINED)으로 조인전략을 사용한다고 지정한다.
- 부모 테이블에서 @DiscriminatorColumn(name = "DTYPE")으로 DTYPE으로 구분 컬럼이 생성된다.
- 자식 엔티티에서 @DiscriminatorValue("A")이라고 작성하면 부모의 DTYPE에 A라고 저장된다.
- 부모 테이블의 Id를 자식 테이블에 그대로 사용하며, 따로 기본키 컬럼명을 설정하고 싶다면 @PrimaryKeyJoinColumn을 사용해서 나타내줄 수 있다.
조인전략의 장단점
장점
- 테이블이 정규화 된다.
- 외래 키 참조 무결성 제약조건을 활용할 수 있다.
- 저장공간을 효율적으로 사용할 수 있다.
단점
- 조회시 조인을 많이 사용하므로 성능이 저하된다.
- 조인을 많이 사용하므로 조회 쿼리가 복잡하다.
- 데이터 저장시에 INSERT SQL을 두 번 실행해야 한다.
단일테이블 전략

말 그대로 테이블을 하나만 쓰는 것이다.
단일테이블도 마찬가지로, DTYPE(구분컬럼)을 통해서 어떤 자식 데이터가 저장되어있는지 알 수 있다.
단일테이블 전략의 장단점
장점
- 조인이 필요없으므로 일반적으로 조회 성능이 빠르다.
- 조회 쿼리 또한 단순하다.
단점
- 자식 엔티티가 매핑한 컬럼은 모두 null을 허용한다.(이게 치명적인 단점)
- 예를 들어 Movie 엔티티에서는 Album과 Book의 컬럼들을 쓰지 않기 때문에 null이 된다.
- 단일 테이블에 모든 것을 저장하므로 테이블이 커질 수 있다.
- 상황에 따라서는 조회 성능이 오히려 느려질 수 있는데 임계점을 넘을 경우이고 이 경우는 흔하지 않다.
단일 테이블은 구분컬럼이 있어야 구분이 가능하므로 구분컬럼은 필수이다.
구현 클래스마다 테이블 전략

자식 테이블에 부모 테이블의 name과 price를 각각 다 가지도록 한다.
- 자식엔티티마다 테이블을 다 만들어 준다.
- 이 전략은 데이터베이스 설계자와 ORM 전문가 둘 다 추천하지 않는다.
- 상속의 개념이 없어져버리기 때문이다.
구현 클래스마다 테이블 전략 장단점
장점
- 서브 타입을 명확하게 구분해서 처리할 때 효과적이다.
- not null 제약조건을 사용할 수 있다.
단점
- 여러 자식 테이블을 함께 조회할 때 성능이 느리다.(UNION SQL이 필요하다.)
- 자식 테이블을 통합해서 쿼리하기 어렵다.
@MappedSuperclass

부모 클래스를 상속 받는 자식 클래스에 매핑 정보만 제공할 때 사용한다.
- 공통 매핑 정보가 필요할 때 사용한다.
- 직접 생성해서 사용할 일은 없고, 단지 매핑 정보를 제공할 목적으로 생성하므로 추상 클래스로 생성할 것을 권장한다.
@MappedSuperclass
public abstract class BaseEntity{
@Id @GenerateValue
private Long id;
private String name;
}
@Entity
public class Member extends BaseEntity{
private String email;
}
@Entity
public class Seller extends BaseEntity{
private String shopName;
}
주의할 점
- 상속관계를 매핑하는 것이 아니다. 헷갈리지 말자.
- 엔티티가 아니며, 테이블과 매핑하는 것도 아니다. 단지 속성 정보만 주는 것이다.
- 조회, 검색이 불가능하다. (em.find(BaseEntity) 불가능), 엔티티가 아니기에 영속성을 가지지 못한다.
- 테이블과 관계없고, 단순히 엔티티가 공통으로 사용하는 매핑정보를 모으는 역할이다.
- 주로 등록일, 수정일, 등록자, 수정자 같은 전체 엔티티에서 공통으로 적용하는 정보를 모을 때 사용한다.
참고로 @Entity 클래스는 엔티티나 @MappedSuperClass로 지정한 클래스만 상속가능하다.
Reference
https://www.inflearn.com/course/ORM-JPA-Basic/dashboard 김영한 님의 JPA 프로그래밍 강의
https://book.naver.com/bookdb/book_detail.nhn?bid=9252528 자바 ORM 표준 JPA 프로그래밍
위의 책을 참고하고, 강의를 수강하면서 작성한 글입니다.
틀린 부분 등 다양한 피드백 환영합니다.