Posts [자바 ORM 표준 JPA 프로그래밍-기본편] 상속관계 매핑
Post
Cancel

[자바 ORM 표준 JPA 프로그래밍-기본편] 상속관계 매핑

본 포스팅은 인프러의 JPA 기본편을 수강하고 정리하는 내용입니다.


상속관계 매핑

  • 관계형 데이터베이스는 상속 관계X
  • 슈퍼타입 서브타입 관계라는 모델링 기법이 객체 상속과 유사
  • 상속관계 매핑: 객체의 상속과 구조와 DB의 슈퍼타입 서브타입 관계를 매핑

image

  • 슈퍼타입 서브타입 논리 모델을 실제 물리 모델로 구현하는 3가지 방법
    • 각각 테이블로 변환 -> 조인 전략
    • 통합 테이블로 변환 -> 단일 테이블 전략
    • 서브타입 테이블로 변환 -> 구현 클래스마다 테이블 전략

주요 어노테이션

  • @Inheritance(strategy=InheritanceType.XXX)
    • JOINED: 조인 전략
    • SINGLE_TABLE: 단일 테이블 전략
    • TABLE_PER_CLASS: 구현 클래스마다 테이블 전략
  • @DiscriminatorColumn(name=“DTYPE”)
    • 뭘 조인해야되는지 아니깐 안넣어도 문제는 없다.
    • 하지만 운영 입장에서 item 테이블만 select 해보면 어떤 타입인지 알 수 없기에 왠만하면 너어주는게 좋다.
  • @DiscriminatorValue(“XXX”)

Note: JPA의 장점으로 테이블 전략을 바꾼다해도 @Inheritance(strategy=InheritanceType.XXX) 값만 변경해주면 된다. mybatis같은 경우엔 수정하는데 비용이 많이 들게 된다. 예를 들어, 조인 전략을 썼다가 성능이 너무 안나와 단일 테이블 전략으로 바꾼다하면 어노테이션 속성값만 바꿔주면 된다.

조인 전략

image

  • Item이라는 테이블을 만들고, 데이터를 나누고 조인으로 데이터를 가져오는 방법
  • insert는 item테이블 album테이블 각각 두 번하고, PK FK로 조인을 통해 데이터를 가져오는 방법
  • ITEM테이블에 구분하는 DTYPE필드를 별도로 두어 어떤 상품 테이블에 속해있는지 지정
  • JPA와 가장 유사한 모델이기도 함
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
@Entity
@Inheritance(strategy = InheritanceType.JOINED)
@DiscriminatorColumn(name = "DTYPE")
public class Item {

    @Id
    @GeneratedValue
    private Long id;

    private String name;
    private int price;

    ...
}

@Entity
@DiscriminatorValue("B")
public class Book extends Item {

    private String author;
    private String isbn;
}

@Entity
@DiscriminatorValue("A")
public class Album extends Item {
    private String artist;
}

@Entity
@DiscriminatorValue("M")
public class Movie extends Item {

    private String director;
    private String actor;

    ...
}

조인 전략의 장단점

  • 장점
    • 테이블 정규화
    • 외래 키 참조 무결성 제약조건 활용가능
      • ITEM_ID를 fk로 활용할 수 있다.
      • 주문에서 item을 봐야될때 ITEM테이블만 join 걸면 된다. (설계가 깔끔해짐)
      • 가격을 가지고 상품을 분류할떄 ITEM테이블만 보면된다.
    • 저장공간 효율화(정규화가 되있기에)
  • 단점
    • 조회시 조인을 많이 사용, 성능 저하
      • 사실 조인이라는게 잘 맞추면 성능이 더 잘 나오기때문에 사실 그리 성능이 저하되지 않는다
      • 저장 공간이 효율화 되있기에 어쩌면 성능이 더 잘 나올수도 있다.
    • 조회 쿼리가 복잡함
    • 데이터 저장시 INSERT SQL 2번 호출
    • 하지만 위의 것들은 그렇게 중요한 단점이 아니라 본다

Note: 기본적으로 조인 전략이 정석이라고 보고 들어가야 한다. 조인 전략이 객체랑도 잘 맞고 정규화도 잘되고 설계입장에서 깔끔하게 떨어져 나온다.

단일 테이블 전략

image

  • 논리 모델을 다 한 테이블로 합치는 방식
  • ITEM테이블에 구분하는 DTYPE필드를 별도로 두어 어떤 상품 테이블에 속해있는지 지정
  • 조인이 필요없다보니 성능상 장점은 있을 수 있음
  • @DiscriminatorColumn(name = “DTYPE”) 어노테이션이 없어도 DTYPE이 필수로 생성된다.
    • 그렇지 않다면 어떤 타입의 데이터인지 구분할 수 없게 된다.
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
@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "DTYPE")
public class Item {

    @Id
    @GeneratedValue
    private Long id;

    private String name;
    private int price;

    ...
}

@Entity
@DiscriminatorValue("B")
public class Book extends Item {

    private String author;
    private String isbn;
}

@Entity
@DiscriminatorValue("A")
public class Album extends Item {
    private String artist;
}

@Entity
@DiscriminatorValue("M")
public class Movie extends Item {

    private String director;
    private String actor;

    ...
}

단일 테이블 전략의 장단점

  • 장점
    • 조인이 필요 없으므로 일반적으로 조회 성능이 빠름
    • 조회 쿼리가 단순함
  • 단점
    • 자식 엔티티가 매핑한 컬럼은 모두 null 허용(중요!)
      • name이랑 price빼고는 모두 null을 허용해줘야됨
      • 데이터 무결성 입장에서 애매한게 있음
    • 단일 테이블에 모든 것을 저장하므로 테이블이 커질 수 있다. 상 황에 따라서 조회 성능이 오히려 느려질 수 있다.

구현 클래스마다 테이블 전략

image

  • Album, Movie, Book 구현 클래스 자체를 각각 테이블로 별도로 구성하는 방식
  • 테이블끼리의 중복된 컬럼들이 가장 많게 됨
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
@Entity
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class Item {

    @Id
    @GeneratedValue
    private Long id;

    private String name;
    private int price;

    ...
}

@Entity
public class Book extends Item {

    private String author;
    private String isbn;
}

@Entity
public class Album extends Item {
    private String artist;
}

@Entity
public class Movie extends Item {

    private String director;
    private String actor;

    ...
}

클래스마다의 테이블 전략은 단순하게 값을 넣고 뺄때는 좋은데 Item클래스로 조회할때 union all 로 다뒤지게 되는 단점이 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Movie movie = new Movie();
movie.setDirector("a");
movie.setActor("bb");
movie.setName("바람과 함께 사라지다");
movie.setPrice(10000);

em.persist(movie);

em.flush();
em.clear();

Item item = em.find(Item.class, movie.getId());
System.out.println("item = " + item);

tx.commit();

image

구현 클래스마다 테이블 전략의 장단점

이 전략은 데이터베이스 설계자와 ORM 전문가 둘 다 추천X.

뭘 묶어내야 시스템이란게 통합이 가능한데 뭔가 묶어내는게 없다. 예를 들어, Album, Movie, Book을 정산해야 한다하면 새로운게 추가 될 때마다 정산 쿼리를 또 작성해야 한다.

  • 장점
    • 서브 타입을 명확하게 구분해서 처리할 때 효과적
    • not null 제약조건 사용 가능
  • 단점
    • 여러 자식 테이블을 함께 조회할 때 성능이 느림(UNION SQL 필요)
    • 자식 테이블을 통합해서 쿼리하기 어려움
    • 변경이라는 관점에서 굉장히 안 좋음. 새로운 타입이 추가될때 기존 것들을 엄청 뜯어내야함
This post is licensed under CC BY 4.0 by the author.

[자바 ORM 표준 JPA 프로그래밍-기본편] 실전 예제3 - 다양한 연관관계 매핑

[자바 ORM 표준 JPA 프로그래밍-기본편] Mapped Supperclass - 매핑 정보 상속