상속은 객체지향 프로그래밍에서 굉장히 중요하다.
하지만 관계형 데이터베이스에는 상속 개념이 존재하지 않는다
RDB에는 상속 개념과 매우 유사한 슈퍼-서브 타입이라는 관계 개념이 존재하는데, JPA가 패러다임의 불일치 해소라는 그 목적성에 맞게 객체지향의 상속을 RDB의 슈퍼-서브 타입으로 잘 매핑해준다.
RDB에서 슈퍼-서브 타입을 표현하는 방식은 세 가지가 있는데 JPA는 이 방법을 모두 지원한다.
1. 조인 전략 (가장 많이 사용됨)
가장 바람직한(?) 모델로 평가받는 조인을 이용한 방식이다.
조인 전략이 구조적으로도 객체지향의 상속을 가장 잘 나타내지 않나 싶다.
부모 클래스의 테이블이 공통속성을 가지고 자식 테이블들은 각자의 고유한 컬럼들을 따로 갖는다.
부모의 PK를 자식의 PK이자 FK로 사용했는데, JAVA에서 자식 객체를 검색할 경우 이를 통해 조인 쿼리를 날려서 찾아낸다.
테이블이 정규화 되어있기 때문에 효율적인 구조라 할 수 있으며 쓸 데 없이 낭비되는 저장공간이 없다는 장점이 있다.
단점으로는 조회할 때 조인 사용량이 높아 성능이 저하될 수 있다는 부분, 새로운 데이터 저장 시 부모, 자식 테이블에 따로따로 insert문을 날려야 한다는 점이 있다.
@Entity
@Inheritance(strategy = InheritanceType.JOINED) // 상속 매핑 전략 선택
@DiscriminatorColumn // 자식 객체를 구분하기 위한 Dtype
public abstract class Item {
// 부모 테이블은 공통된 속성에 대한 컬럼만을 갖는다
@Id @GeneratedValue
private Long id;
private String name;
private int price;
}
2. 단일 테이블 전략 (디폴트)
테이블 하나에다 모든 자식의 정보를 때려넣는 방식이다.
상속 매핑 전략을 따로 설정하지 않으면 이 방식이 기본으로 적용된다.
(다른 자식의 속성 컬럼에 대해선 NULL을 갖는다)
Dtype 컬럼의 존재가 필수적이다. 조인 전략에서는 굳이 Dtype이 없어도 조인을 통해 알아낼 수는 있지만 단일 테이블 전략에서는 Dtype이 없으면 객체를 구분할 수가 없다.
이러한 이유로 @DiscriminatorColumn을 넣지 않아도 자동적으로 Dtype 컬럼이 추가된다.
하나의 테이블에 모든 정보가 다 담겨있기 떄문에 조인 없이 바로 select가 가능하여 조회 성능이 빠르다는 장점이 있다.
단점으로는 다른 자식들에 대한 고유 속성 컬럼은 NULL값으로 가지고 있어야하므로 자식 엔티티의 컬럼들은 전부 다 NULL이 허용되며, 테이블이 너무 커져버릴 수 있다는 점이 있다.
@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE) // 전략 선택 안 하면 디폴트로 싱글 테이블 적용
@DiscriminatorColumn // 자식 객체를 구분하기 위한 Dtype (싱글 테이블에선 명시하지 않아도 자동 생성됨)
public abstract class Item {
@Id @GeneratedValue
private Long id;
private String name;
private int price;
}
3. 테이블 퍼 클래스 전략 (사용하지 말 것!)
부모 클래스에 대한 테이블은 존재하지 않는다.
각 자식 클래스 별로 자신만의 테이블을 갖는데, 이때 부모가 갖고 있어야 할 고유한 속성들을 모든 자식 클래스가 자신의 테이블에 갖고 있는 방식이다.
서브 타입(자식)에 대한 구분이 명확하다는 것과 싱글 테이블 전략과는 다르게 NOT NULL 조건을 사용할 수 있다는 장점이 있다.
단점으로는 하나의 부모에 자식이 여럿일 경우 조회 성능이 굉장히 느려지게 된다는 점이 있다.
각각의 자식 테이블 중에 찾고자 하는 객체가 어느 곳에 있는지 알 수 없기 때문에 모든 테이블을 검사해야 한다.
이를 위해서 UNION 을 사용해 모든 테이블을 합치고 그것을 조회한다.
@Entity
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS) // 웬만하면 사용을 자제해야 하는 전략
@DiscriminatorColumn // 자식 객체를 구분하기 위한 Dtype
public abstract class Item {
@Id @GeneratedValue
private Long id;
private String name;
private int price;
}
위 세 가지 방법 모두 부모 클래스는 abstract로 생성해야 한다.
그렇지 않으면 JPA가 부모 클래스의 개별 테이블까지 함께 만들어버린다.
(Item 클래스는 공통 속성을 한 번에 관리하기 위함일 뿐, Item에 대한 개별 테이블은 필요 없다)
'김영한님 스프링 강의 정리 > JPA' 카테고리의 다른 글
Cascade를 통한 영속성 전이와 고아 객체 (0) | 2021.03.01 |
---|---|
즉시 로딩과 지연 로딩 (0) | 2021.03.01 |
@ManyToMany를 사용하면 안 되는 이유 (0) | 2021.02.24 |
연관관계 매핑과 관계의 주인 (0) | 2021.02.21 |
기본 키 매핑 전략 - IDENTITY, SEQUENCE (0) | 2021.02.21 |