포스트

고급 매핑

고급 매핑

상속관계 매핑


객체의 상속구조DB의 슈퍼타입-서브타입 관계 를 매핑

• 관계형 데이터베이스는 상속 관계가 없다.

• 슈퍼타입-서브타입 관계라는 논리 모델링 기법이 객체 상속과 유사하다.


슈퍼타입-서브타입 논리모델을 실제 물리 모델로 구현하는 방법은 아래와 같다.

방법 전략 주요 어노테이션
각각 테이블로 변환 조인 전략 JOINED
통합 테이블로 변환 단일 테이블 생성 전략(default) SINGLE_TABLE
서브타입 테이블로 변환 구현 클래스마다 테이블 생성 전략 TABLE_PER_CLASS


조인 전략


예시 코드

위 이미지와 같이 DB에 ITEM, ALBUM, MOVIE, BOOK 테이블이 생성된다.

그리고 이 테이블들은 외래키인 ITEM_ID를 통해서 조인 전략 이 수행된다.

1
2
3
4
5
6
7
8
9
10
11
12
@Entity
@Inheritance(strategy = InheritanceType.JOINED)
@DiscriminatorColumn(name = "ITEM_TYPE")
public class Item {

    @Id
    @GeneratedValue
    private Long id;

    private String name;
    private int price;
}
1
2
3
4
5
6
7
8
@Entity
@DiscriminatorValue("M")
public class Movie extends Item {
    private String director;
    private String actor;
    private String name;
    private int price;
}


DB 저장, 조회

▷ 저장

1
2
3
4
5
6
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


▷ 조회

내부적으로 조인을 하여 쿼리를 날린다.

1
Movie findMovie = em.find(Movie.class, movie.getId());
1
2
3
4
5
6
7
8
9
10
11
12
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이라는 테이블에 모든 컬럼이 다 들어간다.

1
2
3
4
5
6
7
8
9
10
11
@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 테이블이 생성되지 않는다.

1
2
3
4
5
6
7
8
9
10
11
@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전략 을 베이스로 가져가고,

만약 테이블이 단순하고 확장 가능성이 없다고 판단되면 단일 테이블 전략을 가져가는게 좋다.

어떤 걸 선택하든 장단점은 있기 때문에 상황에 맞춰 적절한 전략을 선택해야 한다.

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.