고급 매핑
상속관계 매핑
• 객체의 상속구조 와 DB의 슈퍼타입-서브타입 관계 를 매핑
• 관계형 데이터베이스는 상속 관계가 없다.
• 슈퍼타입-서브타입 관계라는 논리 모델링 기법이 객체 상속과 유사하다.
슈퍼타입-서브타입 논리모델을 실제 물리 모델로 구현하는 방법은 아래와 같다.
| 방법 | 전략 | 주요 어노테이션 |
|---|---|---|
| 각각 테이블로 변환 | 조인 전략 | JOINED |
| 통합 테이블로 변환 | 단일 테이블 생성 전략(default) | SINGLE_TABLE |
| 서브타입 테이블로 변환 | 구현 클래스마다 테이블 생성 전략 | TABLE_PER_CLASS |
조인 전략
예시 코드

위 이미지와 같이 DB에 ITEM, ALBUM, MOVIE, BOOK 테이블이 생성된다.
그리고 이 테이블들은 외래키인 ITEM_ID를 통해서 조인 전략 이 수행된다.
@Entity
@Inheritance(strategy = InheritanceType.JOINED)
@DiscriminatorColumn(name = "ITEM_TYPE")
public class Item {
@Id
@GeneratedValue
private Long id;
private String name;
private int price;
}
@Entity
@DiscriminatorValue("M")
public class Movie extends Item {
private String director;
private String actor;
private String name;
private int price;
}
DB 저장, 조회
▷ 저장
Movie movie = new Movie();
movie.setDirector("감독A");
movie.setActor("배우A");
movie.setName("영화A");
movie.setPrice(10000);
em.persist(movie);
DB 테이블에는 아래와 같이 데이터가 들어가게 된다. ID 컬럼이 외래키가 된다.
| ITEM_TYPE | ID | NAME | PRICE |
|---|---|---|---|
| M | 1 | 영화A | 10000 |
| ACTOR | DIRECTOR | ID |
|---|---|---|
| 배우A | 감독A | 1 |
▷ 조회
내부적으로 조인을 하여 쿼리를 날린다.
Movie findMovie = em.find(Movie.class, movie.getId());
select
movie0_.id as id1_5_0_,
movie0_1_.name as name2_5_0_,
movie0_1_.price as price3_5_0_,
movie0_.actor as actor1_8_0_,
movie0_.director as director2_8_0_
from
Movie movie0_
inner join ItemPrac movie0_1_
on movie0_.id=movie0_1_.id
where
movie0_.id=?
어노테이션 설명
| 어노테이션 | 설명 | 비고 |
|---|---|---|
| @Inheritance(strategy = InheritanceType.JOINED) | JOIN전략 | |
| @DiscriminatorColumn(name = “ITEM_TYPE”) | DTYPE 컬럼 생성 | 있는게 좋다. |
| @DiscriminatorValue(“M”) | DTYPE 컬럼에 들어갈 엔티티 이름의 별칭 |
장단점
| 장점 | 단점 |
|---|---|
| 테이블 정규화 | 조회 시 조인을 많이 사용, 성능저하 → 엄청 저하되진 않음 |
| 외래 키 참조 무결성 제약조건 활용가능 | 조회 쿼리가 복잡함 |
| 저장공간 효율화 | 데이터 저장시 INSERT SQL 2번 호출 → 큰 단점 아님 |
단일 테이블 전략
예시 코드

위 이미지와 같이 하나의 ITEM이라는 테이블에 모든 컬럼이 다 들어간다.
@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
public class Item {
@Id
@GeneratedValue
private Long id;
private String name;
private int price;
}
@DiscriminatorColumn없어도 DTYPE 컬럼 생성
장단점
| 장점 | 단점 |
|---|---|
| 조인이 필요 없으므로 일반적으로 조회 성능이 빠름 | 자식 엔티티가 매핑한 컬럼은 모두 Null허용 |
| 조회 쿼리가 단순 | 단일 테이블에 모든 것을 저장하므로 테이블이 커짐(성능 저하-캐바캐) |
구현 클래스마다 테이블 전략
이 전략은 데이터베이스 설계자와 ORM 전문가 둘 다 추천X
예시 코드

각각 ALBUM, MOVIE, BOOK 테이블이 생성이 되는데, 부모 클래스의 필드(id,name,price)가
하위 클래스에 모두 중복으로 들어가게 된다.
그리고 Item을 추상클래스로 정의했기 때문에 ITEM 테이블이 생성되지 않는다.
@Entity
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class Item {
@Id
@GeneratedValue
private Long id;
private String name;
private int price;
}

장단점
| 장점 | 단점 |
|---|---|
| 서브 타입을 명확하게 구분해서 처리할 때 효과적 | 여러 자식 테이블을 함께 조회할 때 성능이 느림(UNION SQL 필요) |
| not null 제약조건 사용 가능 | 자식 테이블을 통합해서 쿼리하기 어려움 |
이 전략은 데이터를 삽입할 때 문제가 되지 않는다. 그런데 Item 타입으로 조회할 때,
UNION ALL로 Item을 상속 받은 모든 테이블을 조회한다. 따라서 성능 저하가 초래된다.
정리
기본적으로 JOIN전략과 단일 테이블 전략을 생각하면서 개발을 하고 DBA와 고민해서
선택을 하는게 좋다. 추천은 기본적으론 JOIN전략 을 베이스로 가져가고,
만약 테이블이 단순하고 확장 가능성이 없다고 판단되면 단일 테이블 전략을 가져가는게 좋다.
어떤 걸 선택하든 장단점은 있기 때문에 상황에 맞춰 적절한 전략을 선택해야 한다.
