상속관계 매핑

OneTwoThree·2023년 7월 26일
0

출처


상속관계를 DB에서 구현하는 방법은 조인 전략, 단일 테이블 전략, 구체 테이블만 만들기 전략이 있다. 어떤 방법을 사용하더라도 JPA서는 모두 매핑이 가능하다. jpa 기본 전략은 Item 테이블에 모든 속성이 들어가는 단일 테이블 전략을 사용한다.

조인 전략

@Entity
@Inheritance(strategy = InheritanceType.JOINED)
public class Item {
    @Id @GeneratedValue
    private Long id;

    private String name;

    private int price;



}

다음과 같이 부모 테이블에 @Inheritnace(strategy = InheritanceType.JOINED) 애노테이션을 달아줘야 조인 테이블 방식으로 매핑이 된다.

@Entity
public class Movie extends Item{

    private String director;
    private String actor;
}

자식 테이블인 Movie 테이블은 다음과 같다.

이 상황에서 Movie 객체를 생성하고 persist 해주면 Item 테이블과 Movie 테이블에 모두 Insert 쿼리가 나간다. 두 row의 Id값은 같다. Movie테이블에서 Id는 FK이면서 PK가 된다.

그리고 이렇게만 하면 DTYPE 컬럼값이 생략된다.
부모 테이블에

@Entity
@Inheritance(strategy = InheritanceType.JOINED)
@DiscriminatorColumn
public class Item {
    @Id @GeneratedValue
    private Long id;

    private String name;

    private int price;
    
}

아래와 같이 @DiscriminatorColumn 애노테이션을 달면 DTYPE 컬럼을 ITEM 테이블에서 볼 수 있다. DB 상에서 부모 테이블인 ITEM 테이블만 보고 자식 테이블 값을 알고 싶다면 이 컬럼을 넣는 것이 좋다. 자식 테이블의 엔티티 명으로 값이 들어간다. 애노테이션에 name 속성값을 줘서 컬럼명을 바꿀 수 있다.
들어가는 값을 바꾸고 싶다면 자식 테이블에 애노테이션을 달아줘야 한다.

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

이런 식으로 @DiscriminatorValue("A")로 DB에서 컬럼에 들어가는 값을 A로 바꿀 수 있다.

  • 장점
    • 테이블이 정규화되어있다
    • 외래 키 참조 무결성 제약조건 활용가능 (?)
    • 저장공간 효율화
    • 객체랑 잘 맞는 정석 전략으로 봐야한다.
  • 단점
    • 조회 시 조인을 많이 사용해서 성능이 저하된다
    • 조회 쿼리가 복잡하다
    • 데이터 저장시 INSERT 쿼리가 두번 호출된다.

단일 테이블 전략

단일 테이블 전략을 사용하기 위해서는 부모 테이블의 애노테이션 속성값만 바꿔주면 된다.

@Inheritance(strategy = InheritanceType.SINGLE_TABLE)

하나의 테이블만 생성된다. 성능이 제일 좋다. 조인할 필요가 없다.
단일 테이블 전략에서는@DiscriminatorColumn 이 없어도 DTYPE 컬럼이 생긴다. 무조건 한 테이블에 들어가기 때문에 DTYPE이 필수로 생성된다.

  • 장점
    • 조인이 필요 없음
    • 조회 쿼리가 단순함
  • 단점
    • 자식 엔티티가 매핑한 컬럼은 모두 null 허용
    • 단일 테이블에 모두 넣어서 테이블이 커질 수 있다.

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

마찬가지로 애노테이션만 바꿔주면 된다.

@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)

이렇게 할 경우 Item 테이블은 생성되지 않는다

  • Item 클래스가 추상 클래스일 경우만 해당됨
  • Item 클래스가 추상 클래스가 아니면 Item 클래스만 독단적으로 사용하는 경우도 있을 수 있으니까 Item 테이블도 생성됨

이 경우는 @DiscriminatorColumn이 의미가 없다
단점은 조회할 때 모든 테이블을 뒤져야함

  • 장점
  • 단점
    • 쓰면 안되는 전략임!

일반적으로 Join 테이블 전략과 단일 테이블 전략 중에 고민하는게 좋다!
정말 단순한 경우 단일 테이블 전략을 사용하는게 좋다.

MappedSuperClass

모든 테이블에 만든 사람, 만든 일자, 수정한 사람, 수정한 일자 정보를 넣어야 한다고 생각해보자.

@MappedSuperclass
public class BaseEntity {

    private String createdBy;
    private LocalDateTime createdDate;
    private String  lastModifiedBy;
    private LocalDateTime lastModifiedDate;
    
    ...Getter, Setter

위와 같이 BaseEntity 클래스를 만들고 @MappedSuperclass 애노테이션을 달아 준다.

다른 클래스에서는 BaseEntity를 extends해서 상속해주면 된다.

상속관계 매핑이랑은 상관이 없고 공통된 필드를 가지고 싶을 때 사용하면 된다.
전체적으로 공통적으로 사용할 속성에 사용해주면 된다.

MappedSuperClass는 엔티티가 아니다. 즉 테이블이 생성되지 않는다.
부모 클래스를 상속받는 자식 클래스에 매핑 정보만 제공한다.

직접 생성해서 사용할 클래스가 아니므로 추상 클래스로 만드는 것을 권장함

예제

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

    @Id
    @GeneratedValue
    @Column(name="ITEM_ID")
    private Long id;
    
    .. 다른 필드들 , Getter, Setter 

Item 클래스를 생성할 일은 없다고 가정하고 abstract 클래스로 선언한다.
이렇게 @Inheritance 애노테이션으로 단일 테이블 전략으로 매핑했다. 그리고 @DiscriminatorColumn 으로 구분할 수 있는 컬럼을 넣어줬다.
이렇게 하고 상속할 클래스들에서 extends만 해주면 끝난다.

@MappedSuperclass
public class BaseEntity {
    private String createdBy;
    private LocalDateTime createdDate;
    private String  lastModified;
    private LocalDateTime lastModifiedDate;

BaseEntity 클래스는 위와 같이 만들어준다.
그리고 모든 클래스가 이 클래스를 상속하도록 하면 된다.
참고로 Item 클래스가 BaseEntity를 상속하게 하면 Item을 상속하는 Movie, Album 등도 BaseEntity 필드들을 갖게 되니까 Movie, Album은 안해도줘도 된다.

0개의 댓글