상속관계 매핑
JPA는 객체 지향 프로그래밍의 상속을 데이터베이스의 슈퍼타입 서브타입 관계로 매핑할 수 있습니다. 이를 통해 상속 구조를 데이터베이스에 저장할 수 있습니다. 상속관계 매핑에는 여러 전략이 있으며, 각각의 장단점이 있습니다.
주요 어노테이션
- @Inheritance(strategy=InheritanceType.XXX)
- JOINED: 조인 전략
- SINGLE_TABLE: 단일 테이블 전략
- TABLE_PER_CLASS: 구현 클래스마다 테이블 전략
- @DiscriminatorColumn(name="DTYPE")
- 상속관계에서 구분 컬럼을 정의할 때 사용합니다.
- @DiscriminatorValue("XXX")
- 각 엔티티가 구분 컬럼에서 가질 값을 정의합니다.
조인 전략 (JOINED Strategy)
장점
- 테이블 정규화가 잘 되어 데이터 중복을 줄입니다.
- 외래 키 참조 무결성 제약조건을 활용할 수 있습니다.
- 저장 공간 효율화에 도움이 됩니다.
단점
- 조회 시 조인을 많이 사용하게 되어 성능이 저하될 수 있습니다.
- 조회 쿼리가 복잡해집니다.
- 데이터를 저장할 때 INSERT SQL이 두 번 호출됩니다.
부모클래스
import javax.persistence.*;
@Entity
@Inheritance(strategy = InheritanceType.JOINED)
@DiscriminatorColumn(name = "DTYPE")
public abstract class Item {
@Id @GeneratedValue
private Long id;
private String name;
private int price;
// getters and setters
}
자식클래스
import javax.persistence.*;
@Entity
@DiscriminatorValue("B")
public class Book extends Item {
private String author;
private String isbn;
// getters and setters
}
@Entity
@DiscriminatorValue("A")
public class Album extends Item {
private String artist;
// getters and setters
}
이 예제에서는 Item 클래스가 부모 클래스이고, Book과 Album이 자식 클래스입니다. @Inheritance 어노테이션으로 상속 전략을 JOINED로 설정하였습니다. @DiscriminatorColumn은 각 엔티티를 구분하기 위해 "DTYPE" 컬럼을 사용하도록 설정합니다. 각 자식 클래스는 @DiscriminatorValue 어노테이션으로 자신만의 구분 값을 가집니다.
단일 테이블 전략 (SINGLE_TABLE Strategy)
단일 테이블 전략은 모든 상속된 엔티티들을 하나의 테이블에 저장하는 방식입니다. 이 전략은 조인이 필요 없어서 조회 성능이 뛰어나지만, 테이블이 커질 수 있습니다.
import javax.persistence.*;
@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "DTYPE")
public abstract class Item {
@Id @GeneratedValue
private Long id;
private String name;
private int price;
// getters and setters
}
@Entity
@DiscriminatorValue("B")
public class Book extends Item {
private String author;
private String isbn;
// getters and setters
}
@Entity
@DiscriminatorValue("A")
public class Album extends Item {
private String artist;
// getters and setters
}
장점
- 조인이 필요 없으므로 일반적으로 조회 성능이 빠름
- 조회 쿼리가 단순함
단점
- 단일테이블에모든것을저장하므로테이블이커질수있다.상황에 따라서 조회 성능이 오히려 느려질 수 있다.
- 자식 엔티티가 매핑한 컬럼은 모두 null 허용
구현 클래스마다 테이블 전략 (TABLE_PER_CLASS Strategy)
이 전략은 각 자식 클래스마다 테이블을 생성하는 방식입니다. 이 전략을 사용하면 각각의 자식 클래스가 자신의 테이블을 가지게 되며, 조인이 필요 없으므로 조회 성능이 좋지만, 테이블 정규화가 안 되어 데이터 중복이 발생할 수 있습니다.
import javax.persistence.*;
@Entity
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class Item {
@Id @GeneratedValue
private Long id;
private String name;
private int price;
// getters and setters
}
@Entity
public class Book extends Item {
private String author;
private String isbn;
// getters and setters
}
@Entity
public class Album extends Item {
private String artist;
// getters and setters
}
이 전략은 데이터베이스 설계자와 ORM 전문가 둘 다 추천X
장점
- 서브 타입을 명확하게 구분해서 처리할 때 효과적
- not null 제약조건 사용 가능
단점
- 여러 자식 테이블을 함께 조회할 때 성능이 느림(UNION SQL 필요)
- 자식 테이블을 통합해서 쿼리하기 어려움
@MappedSuperclass
@MappedSuperclass는 JPA에서 상속관계를 매핑하지 않고, 엔티티들이 공통으로 사용하는 매핑 정보를 모아두기 위해 사용됩니다. 이 어노테이션은 직접 테이블과 매핑되지 않으며, 단순히 매핑 정보를 상속받는 자식 클래스들에 제공하는 역할을 합니다. @MappedSuperclass를 사용하면 부모 클래스의 매핑 정보를 자식 클래스들이 공통으로 사용할 수 있습니다.
주요 특징
- 엔티티가 아니다: @MappedSuperclass로 지정된 클래스는 JPA 엔티티가 아니므로 데이터베이스 테이블과 직접 매핑되지 않습니다.
- 테이블과 매핑되지 않는다: 테이블과 매핑되지 않고, 단순히 매핑 정보를 제공하는 역할을 합니다.
- 공통 매핑 정보 제공: 등록일, 수정일, 등록자, 수정자와 같은 공통 매핑 정보를 모아서 상속받는 자식 엔티티들에 제공할 수 있습니다.
- 직접 조회 불가: @MappedSuperclass로 지정된 클래스를 직접 조회하거나 검색할 수 없습니다 (em.find(BaseEntity) 불가).
- 추상 클래스 권장: 직접 생성해서 사용할 일이 없으므로 추상 클래스로 정의하는 것이 좋습니다.
import javax.persistence.*;
import java.time.LocalDateTime;
@MappedSuperclass
public abstract class BaseEntity {
@Column(name = "created_at", updatable = false)
private LocalDateTime createdAt;
@Column(name = "updated_at")
private LocalDateTime updatedAt;
@PrePersist
protected void onCreate() {
createdAt = LocalDateTime.now();
}
@PreUpdate
protected void onUpdate() {
updatedAt = LocalDateTime.now();
}
// Getters and Setters
public LocalDateTime getCreatedAt() {
return createdAt;
}
public void setCreatedAt(LocalDateTime createdAt) {
this.createdAt = createdAt;
}
public LocalDateTime getUpdatedAt() {
return updatedAt;
}
public void setUpdatedAt(LocalDateTime updatedAt) {
this.updatedAt = updatedAt;
}
}
import javax.persistence.*;
@Entity
public class Member extends BaseEntity {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
// Getters and Setters
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
'JPA' 카테고리의 다른 글
[JPA] JPA 기본값 타입 (0) | 2024.06.30 |
---|---|
[JPA] JPA 프록시와 연관관계 관리 (0) | 2024.06.30 |
[JPA] JPA 연관관계 매핑 (2) (0) | 2024.06.29 |
[JPA] JPA 연관관계 매핑 (1) (0) | 2024.06.29 |
[JPA] JPA 엔티티 매핑 (0) | 2024.06.29 |