테이블 전략

엔티티 상속 구조를 데이터베이스 테이블에 매핑하는 방법

 

1. 단일 테이블 전략

- 하나의 테이블로 생성한다.

- DTYPE으로 어떤 하위 테이블의 데이터인지 구분한다.

- 데이터를 insert했을 때, 다른 엔티티의 공통되지 않은 속성은 null 값이 입력된다.

 

 

2. 조인 전략

- 공통된 데이터를 관리하는 테이블 외에, 그 외 속성을 관리하는 각각의 테이블을 생성한다.

- DB 조회 시 테이블을 조인하는 과정을 거친다.

- 데이터를 입력할 때는 insert 과정이 두 번 필요하다.(product, book 또는 product, coat)

 

 

3. 구현 클래스 전략

- 각각의 테이블로 따로 생성한다.

 


구현 방법

@Inheritance(strategy = InheritanceType.${전략})

  1. JOINED : 조인
  2. SINGLE_TABLE : 단일 테이블(Default)
  3. TABLE_PER_CLASS : 구현 클래스

@DiscriminatorColumn(name = "dtype")

  • dtype 컬럼을 생성한다(관례).
  • 이름 변경이 가능하다.
  • 기본 값 : DTYPE

@DiscriminatorValue("${값}")

  • dtype 값 지정
  • 기본 값 : 클래스 이름

 

1. 단일 테이블 전략

@Entity
@Table(name = "product")
@DiscriminatorColumn(name = "dtype") // dtype 컬럼 생성
public abstract class Product {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;

    private BigDecimal price;

    public Product() {
    }

    public Product(String name, BigDecimal price) {
        this.name = name;
        this.price = price;
    }

}
@Entity
@Table(name = "book")
@DiscriminatorValue(value = "B") // B라는 이름의 dtype 컬럼값 생성 (더 명시적인 이름 사용 필요)
public class Book extends Product {

    private String author;

    public Book() {
    }

    public Book(String author, String name, BigDecimal price) {
        super(name, price);
        this.author = author;
    }
}
@Entity
@Table(name = "coat")
@DiscriminatorValue(value = "C")
public class Coat extends Product {

    private Integer size;

}

해당하지 않는 컬럼은 null

 

2. 조인 전략

- @Inheritance 속성값 외에는 단일 테이블 전략과 코드 동일

- @DiscriminatorColumn을 생략하면 DTYPE이 자동 생성되지 않으므로, 부모 클래스의 기본키를 자식 클래스에서 외래키로 사용해서 조인한다.

2번의 insert문 & 조인해서 데이터 조회

 

 

3. 구현 클래스 전략

- 하위 클래스를 따로 분리하여 관리하기 때문에, 부모 클래스는 실제로 존재하지 않는다.

(부모 클래스는 추상 클래스여야 한다)

- 각 테이블이 별도의 ID 시퀀스를 관리한다.

(동일한 값을 가진 데이터가 여러 테이블에 존재할 수 있어서, GenerationType.IDENTITY로 설정 불가)

- @DiscriminatorColumn, @DiscriminatorValue를 사용할 필요 없음

 

부모 클래스를 통해 데이터를 조회하는 경우에는 아래 이미지처럼 union all을 사용해서 모든 테이블을 조회해야 한다.

-> 구현 클래스 전략은 사용 안 함!

Book book = new Book("wonuk", "spring-advanced", BigDecimal.TEN);
em.persist(book);

Product findProduct = em.find(Product.class, book.getId());

 


정리

JOINED

  • 장점
    1. 테이블 정규화
    2. 외래 키 참조 무결성
    3. 저장공간 효율
  • 단점
    1. 조회시 JOIN을 많이 사용한다.
    2. 데이터 저장시 INSERT SQL 이 2번 호출된다.
    3. SQL Query가 복잡하여 성능이 저하될 수 있다.

SINGLE_TABLE

  • 장점
    1. JOIN을 사용하지 않는다.
    2. 실행되는 SQL이 단순하다.
  • 단점
    1. 자식 Entity가 매핑한 컬럼은 모두 null을 허용한다.
    2. 단일 테이블에 모든 것을 저장하므로 테이블이 커질 수 있다.
    3. 상황에 따라서 조회 성능이 오히려 느려질 수 있다.

TABLE_PER_CLASS

  • 테이블끼리 연관짓기 힘들다, 사용하지 않는것을 권장한다.
  • 장점
    1. 자식 클래스를 명확하게 구분해서 처리할 수 있다.
    2. not null 제약조건 사용이 가능하다.
  • 단점
    1. 여러 자식 테이블을 함께 조회할 때 성능이 느리다.
    2. 부모 객체 타입으로 조회할 때 모든 테이블을 조회해야 한다.

'언어, 프레임워크 > Spring' 카테고리의 다른 글

Bean 생명주기  (1) 2025.04.18
cascade와 orphanRemoval 비교하기  (1) 2025.04.17
JPA 연관관계  (2) 2025.04.16
JPA 성능 최적화 (fetch join, batch size)  (0) 2025.04.16
Converter, Formatter  (0) 2025.04.15