자바생
article thumbnail
Published 2021. 12. 7. 15:41
고급 매핑 Spring 강의/JPA - 기본편
728x90

상속관계 매핑

관계형 DB에는 상속이란 개념이 없고 슈퍼타입 서브타입 관계라는 모델링 기법이 상속 개념과 비슷하다.

 

JPA에서는 상속 관계 매핑의 옵션으로 @Inheritance(strategy = "[전략]")

SINGLE_TABLE, TABLE_PER_CLASS, JOINED 방법이 있다.

 

SINGLE_TABLE TABLE_PER_CLASS JOINED
부모 엔티티에 자식 엔티티의 속성을 모두 넣음. 이 때, 자식 테이블은 생성되지 않음. 부모 엔티티(추상 클래스)가 생성되지 않고, 각각 자식 엔티티가 생성됨. 부모, 자식 엔티티를 각각 테이블로 만들고 PK, FK 를 이용하여 테이블을 조인한다.

 


JOINED

조인 전략은 부모, 자식 엔티티를 모두 테이블로 만들고,

자식 테이블은 부모 테이블의 기본 키를 받는다. PK, FK 전략이다.

쿼리가 나갈 때, 자식 엔티티인 Album과 Book에서 id가 FK라는 것을 알 수 있다.

 

Movie에 데이터를 persist할 때, Item과 Movie에 모두 insert 됨을 알 수 있다.

ID가 1로 매핑이 잘 되어있다.

 

여기서 MOVIE의 PK인 id가 ITEM의 PK 칼럼명과 같게 된다.

@PrimaryKeyJoinColumn 옵션을 사용하여 MOVIE의 PK 칼럼명을 바꿀 수 있다.

@PrimaryKeyJoinColumn(name = "MOVIE_ID")

 

 

 

하지만 ID로는 이 Item이 Movie인지 Book인지 구분할 수 없다. 

 

그래서 아래와 같은 어노테이션을 사용한다면 어떤 타입인지 구별할 수 있게 된다.

디폴트 값은 엔티티명이 들어가게 된다.

@DiscriminatorColumn

자식 클래스에 아래와 같은 어노테이션과 옵션을 줄 수 있다.

@DiscriminatorValue("M")

 

조인 전략은 데이터를 조회할 때 항상 join을 사용하게 된다. (당연한 말)

 


TABLE_PER_CLASS

해당 전략은 부모 테이블을 없애고, 자식 테이블을 필드가 모두 중복되게 만든다.

(이 때 부모 클래스는 추상 클래스이다.)

name, price, id 속성이 모두 자식 테이블에 있는 것을 알 수 있다.

또한, 추상 클래스로 선언된 Item 테이블은 생성되지 않는다.

 

이 때, DTYPE은 필요없게 된다.

왜?

테이블이 모두 구별되어 있기 때문이다.

 

하지만 해당 전략은 매우 큰 단점이 있다.

 

데이터를 조회할 때, 해당 데이터가 있는지 union all 을 이용하여 Item을 상속하는 클래스들을 모두 뒤지게 된다. 따라서 엄청 복잡한 쿼리가 나가게 된다.

 


SINGLE_TABLE

단일 테이블 전략은 default 값으로, 한 테이블 안에 다 때려박는다.

자동으로 DTYPE이 들어가있다.

왜?

DTYPE이 없으면 어떤 자식인지 알 수 없기 때문이다.

조회할 때 조인할 필요 없이 그냥 탐색만 하면 된다.

 

이 전략을 사용하게 될 때 데이터를 넣지 않는 다른 자식 엔티티의 칼럼들은 모두 null이 된다.

예로 Book 데이터를 넣게 되면 나머지 artist나 actor, director 값은 모두 null이 된다.


MappedSuperclass

많은 테이블에 공통적인 속성이 있을 때 사용하면 매우 유용하다.

테이블에 생성날짜, 수정날짜, 생성인, 수정인 이라는 속성이 공통적으로 쓰일 경우,

위 테이블과 다르게 "공통된 속성" 만 가져올 수 있는 방법이다.

-> 부모 클래스(해당 어노테이션이 포함된 클래스) 는 테이블과 매핑하지 않고 자식 클래스에게 매핑 정보만 제공

 

공통된 속성이 포함되어있는 클래스에 @MappedSuperclass 어노테이션을 사용하고,

공통 속성을 포함할 클래스에 위 클래스를 상속받으면 된다.

 

여기서 부모에게 상속받은 컬럼명을 자식 클래스에서 재정의할 수 있다.

@AttributeOverride(name = "createdBy", column = @Column(name = "DELIVERY_CREATED_BY"))

두 개 이상 속성을 바꾸고 싶다면 @AttributeOverrides 어노테이션을 사용하면 된다.

 

 

두 클래스 모두 BaseEntity라는 클래스를 상속한다. 그래서 공통된 속성이 테이블 필드에 있는 것을 알 수 있다.

 

데이터를 조회할 때, 위의 TABLE_PER_CLASS 처럼 em.find를 이용하여 해당 클래스를 조회할 수 없다.

왜?

엔티티로 등록되지 않았기 때문이다.

 

 

현재 Item 은 BaseEntity를 상속하고, Book은 Item을 상속한다. 상속 전략은 조인 전략이다.

보시다시피 book의 속성에는 당연히 BaseEntity의 속성을 사용할 수 있음을 알 수 있다.

 

Category 클래스는 BaseEntity를 상속하지만 다대다 테이블인 위 테이블은 BaseEntity 속성이 들어가있지 않는다.

그래서 강사님께서 다대다를 사용하지 말라고 하신 것이다..

 


상속 매핑에서 JPA 장점

상속 매핑에서 전략에 따라 테이블이 바뀌게 된다. 

하지만 코드를 바꾼 것이 하나도 없다. 

예로 조인 전략을 사용하다가 효율이 나오지 않아 단일 테이블 전략을 사용한다고 생각해보자.

JPA에서는 전략 하나만 바꿔주면 테이블이 생성되기 때문에 매우 편하게 된다.

 


Reference

자바 ORM 표준 JPA 프로그래밍 (김영한 지음)

 

728x90
profile

자바생

@자바생

틀린 부분이 있다면 댓글 부탁드립니다~😀

검색 태그