들어가기 전에
이전글 연관관계 매핑 포스트를 보고 오면 좋습니다.
고급 매핑: 상속관계 매핑
객체지향 프로그래밍에서 상속은 중요한 개념입니다.
이를 데이터베이스와 연동할 때, 어떻게 효과적으로 구현할 수 있을까요?
JPA(Java Persistence API)를 사용하면, 슈퍼타입과 서브타입 관계를 물리 모델로 효율적으로 변환할 수 있습니다.
이를 위한 세 가지 전략이 있습니다:
각각의 테이블로 변환 (조인 전략)
통합 테이블로 변환 (단일 테이블 전략)
서브타입 테이블로 전환 (구현 클래스마다 테이블 전략)
주요 어노테이션
이러한 전략을 구현하기 위해 JPA에서는 다음과 같은 어노테이션을 제공합니다:
@Inheritance(strategy=InheritanceType.XXX):
JOINED: 조인 전략
SINGLE_TABLE: 단일 테이블 전략
TABLE_PER_CLASS: 구현 클래스마다 테이블 전략
@DiscriminatorColumn(name=”DTYPE”): 주로 부모 클래스에서 지정
@DiscriminatorValue(“XXX”): 주로 자식 클래스에서 지정
예를 들어, Animal이라는 부모 클래스와 Cat, Dog라는 자식 클래스가 있다고 가정해 봅시다.
이 경우, Animal 클래스에는 @DiscriminatorColumn을 설정하고, Cat과 Dog 클래스에는 각각 다른 @DiscriminatorValue를 설정합니다.
이렇게 하면 JPA는 Animal 테이블의 각 행이 Cat인지 Dog인지를 구별할 수 있습니다.
예시: Animal, Cat, Dog 클래스
@Entity
@Inheritance(strategy = InheritanceType.JOINED)
@DiscriminatorColumn(name = "ANIMAL_TYPE", discriminatorType = DiscriminatorType.STRING)
public abstract class Animal {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
// getters and setters
}
@Entity
@DiscriminatorValue("CAT")
public class Cat extends Animal {
private boolean likesMilk;
// getters and setters
}
@Entity
@DiscriminatorValue("DOG")
public class Dog extends Animal {
private String breed;
// getters and setters
}
Cat 클래스와 Dog 클래스는 각각 Animal 클래스를 상속받으며, @DiscriminatorValue를 사용해 구별자 값을 설정합니다.
이 구조를 사용하면, 각 자식 엔티티는 자신의 테이블을 가지며, 부모 테이블(Animal)과 조인됩니다.
@DiscriminatorColumn은 각 엔티티 인스턴스의 타입을 식별하는 데 사용됩니다.
전략별 특징과 사용 시 고려사항
-
조인 전략(JOINED):
각 클래스의 데이터를 별도의 테이블에 분리하여 저장합니다.
데이터 모델의 정규화를 유지할 수 있으나, 조인을 필요로 하여 조회 성능에 영향을 줄 수 있습니다. -
단일 테이블 전략(SINGLE_TABLE):
모든 엔티티가 하나의 테이블에 저장됩니다.
조회 성능은 빠르나, 테이블이 커질 수 있으며 null 제약조건 관리에 주의가 필요합니다. -
구현 클래스마다 테이블 전략(TABLE_PER_CLASS):
각 구현 클래스마다 별도의 테이블을 가집니다.
서브 타입을 명확하게 구분할 수 있지만, 여러 테이블을 함께 조회할 때 성능 저하가 발생할 수 있습니다.
결론
상속 관계 매핑은 JPA를 사용하여 객체지향 모델을 데이터베이스 설계에 효과적으로 통합할 수 있는 방법을 제공합니다.
실제 비즈니스적으로 중요하고 복잡할수록 조인 전략을 많이 사용한다고 합니다.
각 전략은 특정 상황에 적합한 장단점을 가지고 있으므로, 상황에 따라 적절한 전략을 선택하는 것이 중요합니다.