Post

연관 관계 고급





참고



상속 관계 매핑


img-description 상속 관계 매핑


  • 관계형 DB에는 상속이란 개념이 없고, 슈퍼타입-서브타입 모델링이 있다.
  • JPA는 RDB의 슈퍼타입-서브타입 모델링을 상속으로 구현할 수 있다.

  • RDB에서 슈퍼타입-서브타입 모델링을 구현할 때 보통 세 가지 전략이 있다.
    • 조인 전략
    • 단일 테이블 전략
    • 복수 테이블 전략



조인 전략


img-description 조인 전략


  • 아이템을 종류별로 각각 정규화된 테이블로 나누는 전략이다.
  • 메인 테이블의 PK를 서브 테이블의 PK면서 FK로 사용한다.
  • @Inheritance(strategy = InheritanceType.JOINED)
  • @DiscriminatorColumn으로 어느 서브 클래스를 가지는지 나타내는 컬럼을 넣어줄 수 있다.
    • 서브 클래스에서는 @DiscriminatorValue로 DTYPE으로 나타낼 값을 정할 수 있다.
    • id 값으로 조인하여 가져오면 되기 때문에 꼭 필수로 넣지는 않아도 된다.
  • 장점
    • 정규화된 테이블
    • 외래키 참조 무결성 제약조건 활용 가능
    • 저장 공간의 효율성
  • 단점
    • 조회 시에 조인이 많아 성능 저하
    • 조회 쿼리가 복잡하다.
    • 삽입 쿼리가 두 번씩 날아간다.



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
@Entity
@Inheritance(strategy = InheritanceType.JOINED)
@DiscriminatorColumn
class Item {

    @Id
    private Long id;

    // ...
}

@Entity
class Album extends Item {
    // ...
}

@Entity
class Movie extends Item {
    // ...
}

@Entity
class Book extends Item {
    // ...
}

  • 이렇게 상속으로 구현할 수 있다.
    • 서브 테이블들은 수퍼 테이블의 PK를 PK로 가진다.
    • JPA 코드에서는 Item의 id를 서브 클래스들이 상속받기 때문에 id를 따로 가지지 않는다.


  • 위에서 구현한 엔티티들로 삽입 쿼리를 날리면,
1
2
3
4
5
6
7
8
9
10
11
12
tx.begin();

Movie movie = Movie.builder()
  .title("babo movie")
  .description("very good")
  .build();

em.persist(movie);

tx.commit();


img-description 조인 전략 삽입 쿼리

img-description 조인 전략 삽입 결과


  • Item 테이블과 Movie 테이블에 각각 한 번씩 총 두 번 insert 쿼리가 날아간 것을 볼 수 있다.
  • 서브 테이블을 나타내는 DTYPE이 MOVIE로 잘 나온 것을 볼 수 있다.


img-description 조인 전략 조회 쿼리


  • Movie를 조회하면 위와 같이 Item 테이블과 Movie 테이블을 조인하여 데이터를 가져오는 것을 볼 수 있다.



단일 테이블 전략


img-description 단일 테이블 전략


  • 하나의 테이블에 모든 컬럼을 넣는 전략이다.
  • 성능의 이점이나 단순하다는 장점이 있다.
  • @Inheritance(strategy = InheritanceType.SINGLE_TABLE)
  • 어느 서브 테이블을 가지는지 구분할 수 없기 때문에, DTYPE이 필수적이다.
    • @DiscriminatorColumn
    • 어노테이션 안붙여도 자동으로 넣어버림
  • 장점
    • 조인이 없으므로, 조회가 빠르다.
    • 조회 쿼리가 단순하다.
  • 단점
    • 서브 클래스의 컬럼들은 모두 nullable이어야 한다.
    • 테이블이 점점 더 커질 우려가 있다.


img-description 싱글 테이블 전략 테이블


  • Item 테이블 하나만 만들면 된다.
    • Movie, Book, Album 등의 서브 테이블에서 사용하는 컬럼 모두를 Item 테이블이 가진다.
    • 위 이미지의 경우 Movie를 넣었기 때문에 Movie와 상관없는 Book과 Album의 컬럼들은 null이다.
  • 딱 봐도 DTYPE이 없으면 구분이 힘들기 때문에 필수적이다.


img-description 싱글 테이블 전략 삽입 쿼리


  • Jpa가 Item 클래스의 모든 서브 클래스들의 컬럼을 모아 쿼리를 날려준다.
  • 하나의 테이블이기 때문에 삽입, 조회 등의 쿼리들이 단순하다.
    • join 없음, 2개 이상 테이블 조회 없음


img-description 싱글 테이블 전략 조회 쿼리


  • Member 엔티티를 조회하면, DTYPE을 사용해 쿼리를 날리는 것을 볼 수 있다.



복수 테이블 전략


img-description 복수 테이블 전략


  • 클래스 당 하나씩 테이블을 만드는 전략이다.
  • 단순하다는 장점이 있다.
  • @Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
  • 서브 테이블이 다 나뉘어져 구분이 바로 되기 때문에 DTYPE이 필요가 없다.
    • 어노테이션 붙여도 안 만들어줌
  • 추천되지 않는 전략
  • 장점
    • 서브 테이블이 명확히 나뉜다는 편리함
  • 단점
    • 서브 테이블을 함께 조회할 때 성능이 좋지 못하다.
    • 테이블 구조 변경에 번거로움


img-description 싱글 테이블 전략 조회 결과


  • Movie 엔티티를 저장하고 조회하면, Movie 테이블에만 로우가 추가된걸 볼 수 있다.


1
2
3
Item item = em.find(Item.class, movieId);

  • 이 전략에서 조회 시에는 주의할 점이 있다.
  • 상속 관계이기 때문에 Item 클래스로 Movie를 조회할 수가 있는데
  • Item은 서브 테이블이 세 가지나 있고, 저 조회 명령에선 어느 서브 테이블의 로우를 찾는지 알 수가 없다.
    • 그래서 서브 테이블을 다 뒤진다.


img-description 싱글 테이블 전략 조회 결과


싱글 테이블 전략 Item 조회 쿼리
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
select
  item0_.id as id1_2_0_,
  item0_.description as descript2_2_0_,
  item0_.artist as artist1_0_0_,
  item0_.author as author1_1_0_,
  item0_.title as title1_3_0_,
  item0_.clazz_ as clazz_0_
from
  ( select
      id,
      description,
      null as artist,
      null as author,
      null as title,
      0 as clazz_
    from
      item
    union
      all select
        id,
        description,
        artist,
        null as author,
        null as title,
        1 as clazz_
      from
        album
    union
      all select
        id,
        description,
        null as artist,
        author,
        null as title,
        2 as clazz_
      from
        book
    union
      all select
        id,
        description,
        null as artist,
        null as author,
        title,
        3 as clazz_
      from
        movie
  ) item0_
where
  item0_.id=?

  • 모든 서브 테이블을 뒤져서 우주주죽 길고 복잡한 쿼리가 날라간다.





@MappedSuperclass


img-description @MappedSuperclass


  • 공통 매핑 정보가 있을 때 사용한다.
    • 서로 다른 테이블이지만 속성만 묶어서 사용하고 싶을 때 사용한다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Entity
class Book {
    // ...

    private LocalDateTime createdAt;
    private LocalDateTime updatedAt;
}

@Entity
class Bookshelf {
    // ...

    private LocalDateTime createdAt;
    private LocalDateTime updatedAt;
}

  • 위처럼 생성 시간, 수정 시간을 모든 엔티티가 공통적으로 알고 있어야 될 때 이렇게 일일이 만들어줘야 한다.
  • 이런 방식은 코드가 지저분해지므로 보기에 좋지 않다.
  • 이럴 때 사용하는 것이 @MappedSuperclass 이다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@MappedSuperclass
class BaseEntity {
    private LocalDateTime createdAt;
    private LocalDateTime updatedAt;
}

@Entity
class Book extends BaseEntity {
    // ...
}

@Entity
class Bookshelf extends BaseEntity {
    // ...
}

  • @MappedSuperclass로 공통적인 부분을 묶어서 만든다.
  • 다른 클래스들이 BaseEntity를 상속받으면 공통적인 컬럼을 한 번에 묶어서 만들 수 있다.


img-description MappedSuperclass 테이블 생성


  • 공통적인 컬럼을 만들기 위한 목적이다.
    • 엔티티가 아님
    • 테이블도 생성되지 않음
    • 직접 생성할 일이 없으므로 추상 클래스로 만드는 것이 추천된다.
  • BaseEntity에도 @Column 등을 적용할 수 있으며, 서브 클래스들 모두의 테이블에 적용된다.

참고로 @Entity 클래스는 @Entity 클래스나 @MappedSuperclass 클래스만 상속 받을 수 있다.





This post is licensed under CC BY 4.0 by the author.