테이블 전략
엔티티 상속 구조를 데이터베이스 테이블에 매핑하는 방법
1. 단일 테이블 전략
- 하나의 테이블로 생성한다.
- DTYPE으로 어떤 하위 테이블의 데이터인지 구분한다.
- 데이터를 insert했을 때, 다른 엔티티의 공통되지 않은 속성은 null 값이 입력된다.
2. 조인 전략
- 공통된 데이터를 관리하는 테이블 외에, 그 외 속성을 관리하는 각각의 테이블을 생성한다.
- DB 조회 시 테이블을 조인하는 과정을 거친다.
- 데이터를 입력할 때는 insert 과정이 두 번 필요하다.(product, book 또는 product, coat)
3. 구현 클래스 전략
- 각각의 테이블로 따로 생성한다.
구현 방법
@Inheritance(strategy = InheritanceType.${전략})
- JOINED : 조인
- SINGLE_TABLE : 단일 테이블(Default)
- 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;
}
2. 조인 전략
- @Inheritance 속성값 외에는 단일 테이블 전략과 코드 동일
- @DiscriminatorColumn을 생략하면 DTYPE이 자동 생성되지 않으므로, 부모 클래스의 기본키를 자식 클래스에서 외래키로 사용해서 조인한다.
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
- 장점
- 테이블 정규화
- 외래 키 참조 무결성
- 저장공간 효율
- 단점
- 조회시 JOIN을 많이 사용한다.
- 데이터 저장시 INSERT SQL 이 2번 호출된다.
- SQL Query가 복잡하여 성능이 저하될 수 있다.
SINGLE_TABLE
- 장점
- JOIN을 사용하지 않는다.
- 실행되는 SQL이 단순하다.
- 단점
- 자식 Entity가 매핑한 컬럼은 모두 null을 허용한다.
- 단일 테이블에 모든 것을 저장하므로 테이블이 커질 수 있다.
- 상황에 따라서 조회 성능이 오히려 느려질 수 있다.
TABLE_PER_CLASS
- 테이블끼리 연관짓기 힘들다, 사용하지 않는것을 권장한다.
- 장점
- 자식 클래스를 명확하게 구분해서 처리할 수 있다.
- not null 제약조건 사용이 가능하다.
- 단점
- 여러 자식 테이블을 함께 조회할 때 성능이 느리다.
- 부모 객체 타입으로 조회할 때 모든 테이블을 조회해야 한다.
'언어, 프레임워크 > 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 |