Skip to content

Latest commit

 

History

History
276 lines (156 loc) · 12.2 KB

7장.md

File metadata and controls

276 lines (156 loc) · 12.2 KB

상속 관계 매핑

관계형 데이터베이스에는 객체지향 언어에서 다루는 상속이라는 개념이 없습니다. 대신에 슈퍼타입 서브타입 관계라는 모델링 기법이 객체지향의 상속과 유사합니다.

스크린샷 2021-08-27 오후 4 47 42

슈퍼타입 서브 타입 논리 모델을 실제 물리 모델로 구현하는 방법은 3가지가 있습니다.

  • 각각의 테이블로 변환: 각각을 모두 테이블로 만들고 조회할 때 조인을 사용합니다. JPA에서는 조인 전략이라 합니다.
  • 통합 테이블로 변환: 테이블을 하나만 사용해서 통합합니다. JPA에서는 단일 테이블 전략이라고 합니다.
  • 서브타입 테이블로 변환: 서브 타입마다 하나의 테이블을 만듭니다.



조인 전략

스크린샷 2021-09-02 오후 3 35 39

조인 전략은 위의 그림처럼 엔티티 각각을 모두 테이블로 만들고 자식 테이블이 부모 테이블의 기본 키를 받아서 기본키 + 외래 키로 사용하는 전략입니다.


장점

  • 테이블이 정규화 됩니다.
  • 외래 키 참조 무결성 제약조건을 활용할 수 있습니다.
  • 저장공간을 효율적으로 사용합니다.

단점

  • 조회할 때 조인이 많이 사용되므로 성능이 저하될 수 있습니다.
  • 조회 쿼리가 복잡합니다.
  • 데이터를 등록할 INSERT SQL을 두 번 실행합니다.

@Entity
public 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을 상속하는 Movie, Album, Book 클래스를 만들었습니다. 이 상태로 실행한 후에 JPA가 만들어주는 DDL문을 보겠습니다.

스크린샷 2021-09-02 오후 3 52 12

기본 전략 자체가 Item 테이블 안에 모든 컬럼을 만든 것을 볼 수 있습니다. 즉, 한 테이블 안에 다 넣는 것이 기본 전략입니다.


스크린샷 2021-09-02 오후 3 56 01

그래서 이번에는 strategy를 JOINED로 주고 실행을 해보겠습니다.


스크린샷 2021-09-02 오후 3 57 39

이번에는 테이블이 모두 각각 생긴 것을 볼 수 있습니다. 위에서 말한 JOIN 전략을 사용한 것입니다.


스크린샷 2021-09-02 오후 4 02 19

그리고 위와 같이 Moview 객체를 저장한 후에 실행해보겠습니다.


스크린샷 2021-09-02 오후 4 03 24

그러면 위와 같이 INSERT 쿼리가 2번 실행된 것을 볼 수 있습니다.


스크린샷 2021-09-02 오후 4 04 37

데이터도 테이블에 잘 들어간 것을 볼 수 있습니다.


스크린샷 2021-09-02 오후 4 07 58

그러면 이번에는 위와 같이 Movie 테이블을 조회하면 JPA가 어떤 쿼리를 만들어줄까요?


스크린샷 2021-09-02 오후 4 09 12

Movie, Item 테이블을 INNER JOIN 쿼리를 생성해주는 것을 볼 수 있습니다.


스크린샷 2021-09-02 오후 4 10 57

위의 조언 전략에 대한 그림에서 보면 DTYPE 이라는 것이 존재하는 것을 보았을텐데요. 이번에는 위와 같이 @DiscriminatorColumn 어노테이션을 사용해서 실행해보겠습니다.


스크린샷 2021-09-02 오후 4 12 45

그러면 위와 같이 DTYPE으로 생성이 되는 것을 볼 수 있습니다.


스크린샷 2021-09-02 오후 4 14 36

@DiscriminatorColumn 어노테이션을 사용하면 DTYPE이 생성되어 Entity 이름이 저장되는 것을 볼 수 있습니다. 사용하여 어떤 엔티티를 통해서 저장된 것이 알기 편하기 때문에 사용하는 것이 좋습니다.



단일 테이블 전략

스크린샷 2021-09-02 오후 3 41 19

단일 테이블 전략은 이름 그대로 테이블을 하나만 사용합니다. ITEM 테이블에 있는 ID와 MOVIE 테이블에 ID랑 똑같은 키입니다.


장점

  • 조인이 필요 없으므로 일반적으로 조회 성능이 빠릅니다.
  • 조회 쿼리가 단순합니다.

단점

  • 자식 엔티티가 매핑한 컬럼은 모두 null을 허용해야 합니다.
  • 단일 테이블에 모든 것을 저장하므로 테이블이 커질 수 있습니다. 그러므로 상황에 따라서는 조회 성능이 오히려 느려질 수 있습니다.

특징

  • 구분 컬럼을 꼭 사용해야 합니다. 따라서 @DiscriminatorColumn을 꼭 설정해야 합니다.
  • 즉, 한 테이블에 모든 정보를 다 저장하고 DTYPE을 통해서 구분하는 것입니다.

스크린샷 2021-09-02 오후 4 20 39

위와 같이 strategy 하나만 변경하면 됩니다.


스크린샷 2021-09-02 오후 4 21 28

그러면 위와 같이 테이블 하나에 모든 정보를 다 생성하는 것을 볼 수 있습니다. (다른 테이블은 생성되지 않았습니다.)


스크린샷 2021-09-02 오후 4 22 39

그리고 실행되는 쿼리만 보아도 INSERT 쿼리도 1번만 실행되고 SELECT 쿼리도 JOIN 필요 없이 간단하게 가져오는 것을 볼 수 있습니다. 참고로 단일 테이블 전략은 @DiscriminatorColumn 어노테이션을 사용하지 않아도 DTYPE이 자동으로 생성됩니다.



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

스크린샷 2021-09-02 오후 3 42 27

구현 클래스마다 테이블 전략은 위와 같이 자식 엔티티마다 테이블을 만듭니다. 즉, Item 테이블을 없애고 NAME, PRICE 같은 속성들은 중복되도록 허용하는 것입니다. (일반적으로 추천하지 않는 전략입니다.)


장점

  • 서브 타입을 구분해서 처리할 때 효과적입니다.
  • not null 제약 조건을 사용할 수 있습니다.

단점

  • 여러 자식 테이블을 함께 조회할 때 성능이 느립니다.(SQL에 Union 사용..)
  • 자식 테이블을 통합해서 쿼리하기 어렵습니다.

스크린샷 2021-09-02 오후 4 27 51

위와 같이 TABLE_PER_CLASS 속성을 주면 됩니다. 그리고 Item 클래스는 지금까지 추상 클래스로 안만들었지만 추상 클래스로 사용해야 합니다.


스크린샷 2021-09-02 오후 4 33 34

위와 같이 Movie Key로 Item을 찾아오면 어떤 쿼리가 실행될까요?


스크린샷 2021-09-02 오후 4 34 32

엄청나게 복잡한 UNION을 사용한 쿼리가 발생하는 것을 볼 수 있습니다. 즉, 이 전략은 추천하지 않기에 다른 전략을 사용하는 것이 좋습니다.(사용하면 안되는 전략..)



@MappedSuperclass

스크린샷 2021-09-02 오후 4 37 07

지금까지 학습한 상속 관계 매핑은 부모 클래스와 자식 클래스를 모두 데이터베이스 테이블과 매핑했습니다. 부모 클래스는 테이블과 매핑하지 않고 부모 클래스를 상속 받는 자식 클래스에게 매핑 정보만 제공하고 싶으면 @MappedSuperclass을 사용합니다.


예제 코드

스크린샷 2021-09-02 오후 4 41 43

만약에 위와 같이 모든 테이블에 생성 시간, 변경 시간을 저장해야 한다는 요구사항이 생겼다면 어떻게 할 수 있을까요? 위와 같이 모든 엔티티에 필드를 다 추가해야 할까요?

이럴 때 사용하면 좋은 것이 @MappedSuperclass입니다.

스크린샷 2021-09-02 오후 4 43 52

위와 같이 BaseEntiy라는 부모 클래스를 만들고 @MappedSuperclass 어노테이션을 추가한 후에 공통적으로 사용할 컬럼들을 적어주면 됩니다.


스크린샷 2021-09-02 오후 4 44 52

그리고 위와 같이 공통 필드를 사용할 엔티티는 BaseEntity 클래스를 상속 받으면 됩니다.

스크린샷 2021-09-02 오후 5 37 48

그러면 위와 같이 Member 테이블을 생성할 때 자동으로 BaseEntity에 존재하는 필드들이 생성되는 것을 볼 수 있습니다.



@MappedSuperclass 특징

  • 테이블과 매핑되지 않고 자식 클래스에 엔티티의 매핑 정보를 상속하기 위해 사용합니다.
  • @MappedSuperclass로 지정한 클래스는 엔티티가 아니므로 em.find()나 JPQL에서 사용할 수 없습니다.
  • 이 클래스를 직접 생성해서 사용할 일은 거의 없으므로 추상 클래스로 만드는 것을 권장합니다.

즉, @MappedSuperclass는 테이블과는 관계가 없고 단순히 엔티티가 공통으로 사용하는 매핑 정보를 모아주는 역할을 할 뿐입니다.