JPA의 상속 매핑은 객체지향의 계층 구조를 관계형 데이터베이스에 매핑하는 방법을 제공한다. 각 전략은 성능, 데이터 무결성, 유지보수성 측면에서 장단점이 있으며, 적절한 선택이 성능에 큰 영향을 미친다.
•
단일 테이블 전략 (SINGLE_TABLE):
◦
설명: 모든 상위 및 하위 클래스 엔티티를 단일 테이블에 저장. 클래스 구분을 위해 판별자 컬럼(@DiscriminatorColumn, 기본값 DTYPE) 사용.
◦
장점:
▪
단일 테이블로 쿼리 성능이 빠르다(조인 불필요).
▪
간단한 계층 구조에 적합.
◦
단점:
▪
하위 클래스별 고유 속성은 NULL 허용 컬럼으로 저장, 데이터 무결성 제약(NOT NULL) 사용 불가.
▪
테이블 크기가 커질수록 관리 복잡성 증가.
◦
실무 예시: 소규모 상속 계층(예: Notification → EmailNotification, SmsNotification)에서 사용. 예:
@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "notification_type")
public abstract class Notification {
@Id @GeneratedValue
private Long id;
private String firstName;
private String lastName;
}
@Entity
@DiscriminatorValue("EMAIL")
public class EmailNotification extends Notification {
private String emailAddress;
}
Java
복사
◦
성능 고려사항: 쿼리 성능은 우수하나, 테이블 크기가 커지면 인덱스 효율성 저하 가능.
•
조인 전략 (JOINED):
◦
설명: 상위 클래스와 각 하위 클래스마다 별도 테이블 생성, 조인으로 데이터 결합. @Inheritance(strategy = InheritanceType.JOINED) 사용.
◦
장점:
▪
데이터 정규화로 무결성 유지(NOT NULL 제약 가능).
▪
@ManyToOne 다형성 쿼리에 적합.
◦
단점:
▪
조회 시 조인 쿼리로 성능 저하 가능.
▪
다형성 쿼리(예: SELECT n FROM Notification n)는 복잡한 조인 발생.
◦
실무 예시: 데이터 무결성이 중요한 경우(예: Publication → Book, BlogPost) 사용.
@Entity
@Inheritance(strategy = InheritanceType.JOINED)
public abstract class Publication {
@Id @GeneratedValue
private Long id;
private String title;
private LocalDate publishingDate;
}
@Entity
public class Book extends Publication {
private int pages;
}
Java
복사
◦
성능 고려사항: @ManyToOne 관계에서 Strategy 패턴과 결합하면 다형성 처리 효율적. 그러나 조인 쿼리 최적화(예: 인덱스, JOIN FETCH) 필요.
•
구체 클래스당 테이블 전략 (TABLE_PER_CLASS):
◦
설명: 각 구체 클래스마다 독립된 테이블 생성. @Inheritance(strategy = InheritanceType.TABLE_PER_CLASS) 사용.
◦
장점: 테이블 구조 단순, 정규화 불필요.
◦
단점:
▪
다형성 쿼리 시 모든 테이블에 대해 UNION 쿼리 발생, 성능 저하.
▪
중복된 속성 매핑으로 유지보수 복잡.
◦
실무 예시: 드물게 사용. 다형성 쿼리가 필요 없는 경우 제한적으로 고려.
◦
성능 고려사항: UNION 쿼리로 인해 대규모 데이터 처리 시 비효율적, 사용 지양 권장.
•
매핑된 슈퍼클래스 (@MappedSuperclass):
◦
설명: 상속 계층이 아닌 공통 속성 재사용을 위해 사용. 데이터베이스 테이블에 직접 매핑되지 않음.
◦
장점: 공통 속성(예: 생성일, 수정일)을 여러 엔티티에서 재사용 가능.
◦
단점: 다형성 쿼리 지원 불가.
◦
실무 예시: 감사 로그 속성 공유 시 사용. 예:
@MappedSuperclass
public abstract class Auditable {
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
}
@Entity
public class Post extends Auditable {
@Id @GeneratedValue
private Long id;
private String title;
}
Java
복사
◦
성능 고려사항: 다형성 필요 없는 경우 간단하고 효율적.
6.2 실무 적용
•
전략 선택 기준:
◦
데이터 접근 패턴: 조회 빈도가 높으면 SINGLE_TABLE, 무결성 우선이면 JOINED.
◦
성능 요구사항: 다형성 쿼리가 많으면 JOINED 또는 SINGLE_TABLE 선호.
◦
유지보수성: 복잡한 계층 구조는 JOINED로 정규화.
•
성능 최적화:
◦
다형성 쿼리 최적화: JOIN FETCH 또는 @NamedEntityGraph로 N+1 문제 방지.
◦
판별자 컬럼 인덱싱: SINGLE_TABLE에서 DTYPE 컬럼에 인덱스 추가.
◦
Hypersistence Optimizer로 상속 매핑 문제(예: 비효율적 조인) 진단.
•
실무 팁:
◦
상속은 주로 행동 패턴(Strategy, Visitor) 구현에 적합. 데이터 구조 재사용은 컴포지션 선호.
◦
SINGLE_TABLE은 소규모 계층, JOINED는 복잡한 계층에 적합. TABLE_PER_CLASS는 피하는 것이 좋다.
6.3 권장사항
•
상속 계층은 최소화하여 쿼리 복잡성 감소.
•
SINGLE_TABLE은 간단한 계층, JOINED는 무결성 우선 시스템에 사용.
•
다형성 쿼리 최적화를 위해 @NamedEntityGraph 또는 JOIN FETCH 활용.
•
Hypersistence Optimizer로 상속 매핑의 성능 문제 분석.