테이블 전략
JPA에서 엔티티 상속 구조를 데이터베이스 테이블에 매핑하는 방법을 말한다.
JPA는 엔티티의 상속 구조를 처리하기 위해 3가지의 테이블 전략을 제공한다.
각각의 전략을 데이터 저장 방식과 성능에 차이가 있으므로 프로젝트의 요구사항에 맞게 선택할 수 있다.
💡 참고
관계형 데이터베이스의 테이블에는 상속 관계가 없다.
테이블로 클래스 상속관계 구현 방법
위는 상속 관계에 있는 클래스들의 관계를 나타낸다.
이 관계를 테이블로 나타낼 수 있는 다양한 전략들에 대해 알아본다.
1. 조인 전략
각각에 대한 클래스를 테이블로 만들고 외래키로 연관관계를 구성한다.
Product 테이블의 DTYPE(Book OR Coat)으로 어떤 테이블의 데이터인지 구분한다.
2. 단일 테이블 전략
모든 정보를 가진 하나의 테이블로 만든다.
DTYPE으로 어떤 테이블의 데이터인지 구분한다.
author 혹은 size 가 null 이 허용된다.
- 데이터베이스 관점에서 null 있다는 것은 좋지 않다.
3. 구현 클래스
구현 클래스 각각을 테이블로 만든다.
JPA의 테이블 전략
JPA는 위의 모든 전략으로 테이블을 구현할 수 있도록 지원한다.
Annotation
JPA의 테이블 전략에 쓰이는 어노테이션은 다음과 같다.
@Inheritance(strategy = InheritanceType.${전략})
- JOIEND : 조인
- SINGLE_TABLE : 단일 테이블(Default)
- TABLE_PER_CLASS : 구현 클래스
@DiscriminatorColumn(name = "dtype")
- dtype 컬럼을 생성한다
- 이름을 dtype으로 쓰는 것이 관례이다.
- 이름 변경이 가능하다.
- 기본값 : DTYPE
@DiscriminatorValue("${값}")
- dtype의 값을 지정한다
- 기본값 : 클래스 이
각 전략의 특징
SINGLE_TABLE
단일 테이블 전략에서 @DiscriminatorColumn을 선언해 주지 않아도 기본으로 DTYPE 컬럼이 생성된다.
한 테이블에 모든 컬럼을 저장하기 때문에 DTYPE 없이는 테이블을 판단할 수 없다.
JOINED
조인 전략에서 @DiscriminatorColumn을 선언하지 않으면 DTYPE 컬럼이 생성되지 않는다.
JOIN을 통해 테이블을 구분할 수 있지만, DTYPE 컬럼을 넣어주는 것이 명확하다.
TABLE_PER_CLASS
부모 클래스의 테이블은 실제 생성되는 테이블이 아니기 때문에 반드시 abstract 추상 클래스여야 한다.
구현 클래스 전략에서는 상속 관계를 무시하고 각 테이블이 별도의 ID 시퀀스를 관리해야 한다. 동일한 @Id 값을 가진 데이터가 여러 테이블에 존재할 수 있다.
따라서, GenerationType.IDENTITY 를 사용하지 못한다.
TABLE_PER_CLASS 전략 문제점
public class Main {
public static void main(String[] args) {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("entity");
EntityManager em = emf.createEntityManager();
EntityTransaction transaction = em.getTransaction();
transaction.begin();
try {
Book book = new Book("wonuk", "spring-advanced", BigDecimal.TEN);
em.persist(book);
em.flush();
em.clear();
Product findProduct = em.find(Product.class, book.getId());
transaction.commit();
} catch (Exception e) {
transaction.rollback();
} finally {
em.close();
}
emf.close();
}
실행 결과
부모 클래스로 자식 엔티티를 조회하면 모든 테이블을 조회해야 한다.
테이블 전략 장단점
JOINED
장점
- 테이블 정규화
- 외래 키 참조 무결성
- 저장공간 효율
단점
- 조회시 JOIN을 많이 사용한다
- 데이터 저장시 INSERT SQL이 2번 호출된다.
- SQL Query가 복잡하여 성능이 저하될 수 있다.
SINGLE_TABLE
장점
- JOIN을 사용하지 않는다.
- 실행되는 SQL이 단순하다.
단점
- 자식 Entity가 매핑한 컬럼은 모두 null을 허용한다.
- 단일 테이블에 모든 것을 저장하므로 테이블이 커질 수 있다.
- 상황에 따라서 조회 성능이 오히려 느려질 수 있다.
TABLE_PER_CLASS
테이블끼리 연관짓기 힘들다, 사용하지 않는 것을 권장한다.
장점
- 자식 클래스를 명확하게 구분해서 처리할 수 있다
- not null 제약조건 사용이 가능하다
단점
- 여러 자식 테이블을 함께 조회할 때 성능이 느리다
- 부모 객체 타입으로 조회할 때 모든 테이블을 조회해야 한다.
선택 기준
- 비즈니스적으로 복잡하다 = JOINED
- 객체지향적인 개발에 어울리는 방법
- 단순하고 확장 가능성이 없다 = SINGLE_TABLE
- 두 방법의 장단점을 구분하여 상황에 맞는 선택을 해야한다.
💡 참고
설계상 TABLE_PER_CLASS 와 같이 단순히 공통 컬럼이 필요하다면 @MappedSuperclass를 사용하면 된다.
JPA Auditing 활용
'JPA' 카테고리의 다른 글
Proxy (0) | 2024.11.28 |
---|---|
N:M 연관관계 (0) | 2024.11.27 |
1:1 연관관계 (1) | 2024.11.27 |
1:N 연관관계에서 1이 연관관계 주인일 때 (0) | 2024.11.27 |