[JPA] 상속관계매핑 (JPA 기본편 by 김영한)

su_y2on·2022년 1월 26일
0

JPA

목록 보기
8/17
post-thumbnail

상속관계 매핑


관계형 데이터 베이스상속 관계가 없습니다. 하지만 상속관계와 유사한 모델링 기법인 슈퍼타입과 서브타입이 있습니다. 슈퍼타입은 서브타입들의 공통적인 필드들을 뽑아내어 만들고 서브타입은 슈퍼타입보다는 구체적입니다. 아래 그림의 왼쪽이 DB에서의 슈퍼,서브타입개념이며 오른쪽이 객체의 상속관계입니다.

이 두 관계를 매핑해주는 방법은 크게 3가지가 있습니다.


1. 조인전략

먼저 조인전략은 아래의 오른쪽 그림처럼 DB가 만들어지도록 객체를 구현하는 것입니다. 가장 두 관계가 유사한 경우입니다.

이제 코드로 직접 Entity를 생성해보도록 하겠습니다. 먼저 ITEM 엔티티는 직접 만들일이 없기 때문에 추상(abstract)클래스로 만들어줍니다. 그리고 공통적인 필드들을 넣어주고 상속전략을 JOINED로 해줍니다. @DiscriminatorColumn은 DTYPE을 만들어주는 어노테이션으로 어떤 구체적인 서브타입(Movie, Book, Album...)인지 알려줍니다. 이는 나중에 item을 조회할 때 어떤 서브타입과 연결된 것인지 바로 알아보기 좋기때문에 붙여주는 것이 좋습니다.

@Entity
@Inheritance(strategy =  InheritanceType.JOINED)
@DiscriminatorColumn // DTYPE
public abstract class Item {

    @Id @GeneratedValue
    private Long id;

    private String name;
    private int price;
    
 }
 

아래와 같이 Book엔티티는 extends를 이용해서 Item을 상속받으면 됩니다.

@Entity
public class Book extends Item{

    private String author;
    private String isbn;
}

이제 서브타입 객체가 만들어지면 그 PK값이 ITEM객체의 PK값이면서 서브타입의 FK가 됩니다. Movie로 테스트를 해보면 아래와 같이 결과가 나옵니다

Movie movie = new Movie();
movie.setDirector("A");
movie.setActor("BBB");
movie.setName("바함사");
movie.setPrice(10000);
em.persist(movie);
em.flush();

조인전략의 장단점은 아래와 같으며 가장 객체의 상속관계와 유사하게 구현할 수 있어서 직관적입니다. 스케일이 크거나 주요모델일 경우 조인전략을 사용하는 것이 좋습니다.

장점

저장공간의 효율화
테이블 정규화

단점

조회시 조인이 필요해서 성능저하
데이터 저장시에 insert쿼리 두번나감 (item 하나, movie 하나)



2. 단일 테이블 전략

단일 테이블은 모든 구현체를 합쳐서 하나의 테이블에 넣어버리는 것입니다.

Item부분에서 타입만 SINGLE_TABLE로 바꿔주면 됩니다. 여기서는 @DiscriminatorColumn를 붙여주지 않아도 DTYPE이 알아서 생깁니다. 왜냐하면 하나의 테이블로 합쳐졌기때문에 구분이 필요하기 때문입니다.

@Entity
@Inheritance(strategy =  InheritanceType.SINGLE_TABLE)
public abstract class Item 

아까와 같이 Movie객체를 생성해서 쿼리를 날리면 테이블에 아래와 같이 들어간 것을 확인할 수 있습니다.

Movie객체가 필요한 필드를 제외하고 모두 null로 처리되어있습니다. 단일테이블전략의 장단점은 아래와 같고 작은 스케일의 상속관계에 적용하면 좋습니다.

장점

조회시 조인이 필요없기 때문에 조회성능이 좋음

단점

컬럼에 null이 들어감
테이블이 커질 수 있음




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

이 전략은 쓰면 안되는 전략입니다! 상속의 의미가 사실상 없어지고 trade off가 딱히 없는? 전략이기 때문입니다.

장점

서브타입을 명확하게 구분가능
not null 조건 사용가능

단점

여러 자식 테이블을 조회할 때 성능이 느리다 (모두 다 찾음)




@MappedSuperclass

DB를 설계하다보면 공통적으로 필요한 컬럼들이 있습니다. 예를 들면 작성자, 수정일자, 작성일자, 수정자..입니다. 이는 꼭 서비스에 보여질 필요가 없더라도 운영상 필요할 수 있기 때문에 대부분의 테이블에 넣어주기도 합니다. 이 컬럼들 하나하나 다 복붙으로 모든 테이블에 넣어주기엔 너무 번거롭습니다.. 이럴때 딱 따로 빼서 쏙쏙 상속을 해준다면???✨✨✨



그렇게 할 수 있는 방법이 바로 @MappedSuperclass입니다. 아래와 같이 공통으로 필요한 컬럼들만 묶어서 BaseEntity로 만들어줍니다. 여기서 주의해야할점은 사실 이 클래스는 엔티티가 아니라는 것입니다!!! (@Entity가 없죠?) 그냥 공통으로 적용하기위한 친구인것이죠ㅎㅎ 그래서 따로 만들일도 없기때문에 abstract처리해줍니다.

@MappedSuperclass
public abstract class BaseEntity {

    private String createdBy;
    private LocalDateTime createdDate;
    private String lastModifiedBy;
    private LocalDateTime lastModifiedDate;
    
 }




상속시켜주는 방법은 아래처럼 필요한 엔티티로가서 extends해주면 됩니다. 정말 편리하죠??

@Entity // JPA가 인식
public class Member extends BaseEntity 

그럼 Member테이블이 생성될 때 상속받은 필드들이 쏙 만들어집니다. 하지만 아까 말했듯이 Entity가 아니기때문에 따로 BaseEntity는 테이블이 생성되지않습니다!

Hibernate: 
    
    create table Member (
       MEMBER_ID bigint not null,
        createdBy varchar(255),
        createdDate timestamp,
        lastModifiedBy varchar(255),
        lastModifiedDate timestamp,
        USERNAME varchar(255),
        LOCKER_ID bigint,
        TEAM_ID bigint,
        primary key (MEMBER_ID)
    )

0개의 댓글