본문 바로가기

Back-End/JPA

상속관계 매핑(JPA)

관계형 데이터베이스는 객체의 상속 개념이 없다. 대신 슈퍼타입 - 서브타입 모델링 기법을 사용한다. JPA에서는 이를 상속관계 매핑을 통해 지원함.


1. 슈퍼타입 - 서브타입 논리 모델을 물리 모델로 변환하는 방법

조인 전략 (일단 조인 전략을 기본으로 선택!)

  • 여러 테이블로 나누어 저장하고 JOIN을 통해 데이터를 조회하는 전략입니다.
    • 장점
      • 테이블이 정규화되어 데이터 중복이 없다.
      • 외래 키 참조 무결성 제약조건 활용 가능
      • 저장공간 효율화
    • 단점
      • 조회시 JOIN 연산이 많이 사용, 성능 저하
      • 조회 쿼리가 복잡함
      • 데이터 저장시 INSERT SQL 2번 호출하지만, 큰 성능 문제는 아님.
    • 예시 코드(조인 전략)
import jakarta.persistence.*;

@Entity
@Inheritance(strategy = InheritanceType.JOINED) // 조인 전략
@DiscriminatorColumn(name = "DTYPE") // 구분 컬럼 추가
public abstract class Item {
    @Id @GeneratedValue
    private Long id;
    private String name;
}

@Entity
@DiscriminatorValue("BOOK")
public class Book extends Item {
    private String author;
}

@Entity
@DiscriminatorValue("MOVIE")
public class Movie extends Item {
    private String director;
}
  • 조인 전략 설명
    • 부모 클래스인 Item의 공통 속성은 Item 테이블에 저장
    • 자식 클래스 Book과 Movie의 고유 속성은 각각 Book 테이블과 Movie 테이블에 저장된다.
    • 조회 시 JOIN을 통해 데이터를 합쳐서 가져온다.

단일 테이블 전략(SINGLE _TABLE)

  • 하나의 테이블에 모든 데이터를 저장하는 전략
  • 장점
    • JOIN이 필요 없으므로 조회 성능이 빠르다.
    • 조회 쿼리가 단순하다.
  • 단점
    • 자식 엔티티에 매핑된 컬럼은 null 값을 허용해야 해서 치명적이다.
    • 하나의 테이블에 모든 데이터를 저장하므로 테이블이 커질 수 있다.
  • 예시 코드(단일 테이블 전략)
import jakarta.persistence.*;

@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE) // 단일 테이블 전략
@DiscriminatorColumn(name = "DTYPE") // 구분 컬럼 추가
public abstract class Item {
    @Id @GeneratedValue
    private Long id;
    private String name;
}

@Entity
@DiscriminatorValue("BOOK")
public class Book extends Item {
    private String author;
}

@Entity
@DiscriminatorValue("MOVIE")
public class Movie extends Item {
    private String director;
}
  • 단일 테이블 전략 설명
    • 하나의 테이블(Item)에 부모와 자식 엔티티의 모든 데이터를 저장한다.
    • DTYPE 컬럼을 통해 각 행이 어떤 엔티티인지 구분한다.
  • 사실 전략을 바꿀 때는 그냥 InheritenceType만 바꿔주면 된다

2. 공통 매핑 정보(MappedSuperclass)

공통된 매핑 정보가 필요할 때 사용한다. 예를 들어, createdAt(생성일), updatedAt(수정일) 같은 공통 필드가 필요하면 @MappedSuperclass를 활용하면 된다.

  • 특징 
    • 엔티티가 아니며 테이블과 매핑되지 않는다.
    • 자식 엔티티에 공통 매핑 정보만 제공
    • 조회, 검색 불가하므로 추상 클래스로 만드는 것이 일반적
@Getter
@MappedSuperclass // 공통 매핑 정보 제공
@EntityListeners(AuditingEntityListener.class) // 자동 날짜 설정
public abstract class BaseEntity {

    @CreatedDate
    @Column(updatable = false)
    private LocalDate createdAt;

    @LastModifiedDate
    private LocalDate updatedAt;
}

@Entity
public class Product extends BaseEntity {
    @Id @GeneratedValue
    private Long id;
    private String name;
}
  • BaseEntity는 공통된 필드인 createdAt과 updatedAt을 제공한다.
  • Product 엔티티는 BaseEntity를 상속 받아 공통 매핑 정보를 사용한다.

3. 주요 애너테이션 정리

  • @Inheritance(strategy = InheritanceType.XXX)
    • 상속관계 매핑 전략을 지정합니다.
      • JOINED: 조인 전략
      • SINGLE_TABLE: 단일 테이블 전략
  • @DiscriminatorColumn(name = "DTYPE")
    • 구분 컬럼을 추가하여 각 행이 어떤 엔티티인지 구분합니다.
  • @DiscriminatorValue("XXX")
    • 각 자식 엔티티의 구분 값을 설정합니다.
  • @MappedSuperclass
    • 공통 매핑 정보를 제공할 때 사용합니다.