[JPA] 객체간의 매핑
상속
DB에는 상속 개념이 없지만 DB모델링 기법중 슈퍼타입 - 서브타입 모델링 기법이 상속 개념과 비슷하다.
1. 조인전략
- 각각의 엔티티를 만든다.
- 자식 테이블은 부모 테이블의 기본 키를 받아서 기본 키 + 외래 키로 사용하는 전략
- 부모 테이블에서는 자식테이블을 구분하는 컬럼을 생성
Character |
ID 이름 능력치 Jobs (자식 구분 컬럼) |
Warrior | Mage | Rogue |
Character_ID (PK, FK) 힘(power) |
Character_ID (PK, FK) 지능(intellect) |
Character_ID (PK, FK) 행운(luck) |
<부모 클래스>
@Entity
@Inheritance(strategy = InheritanceType.JOINED)
@DiscriminatorColumn(name = "JOBS")
public abastract class Charecter {
....
}
<자식 클래스>
@Entity
@DiscriminatorValue("A")
//@PrimaryKeyJoinColumn(name = "WARRIOR_ID")
public class Warrior extends Character {
....
}
@Inheritance
- 상속 매핑시 부모클래스에 작성되는 어노테이션
- 매핑 전략 지정
@DiscriminatorColumn
- 부모 클래스에서 사용되는 구분 컬럼명
- JPA 표준명세에는 작성하라고 명시되어 있지만, 몇몇 구현체에서 생략가능
@DiscriminatorValue
- 자식 클래스에서 부모클래스의 구분 컬럼명에 저장될 값
@PrimaryKeyJoinColumn
- 자식 테이블의 기본 키 컬럼명을 변경할 때 사용되는 어노테이션
- 기본값은 부모 테이블의 ID 컬럼명
조인 전략의 장/단점
장점
- 정규화된 테이블과 외래키 참조 무결성 ( RDB의 장점 )
- 불필요한 저장공간을 없앨 수 있다.
단점
- 조인으로 인한 성능 저하
- 조회 쿼리의 복잡
- INSERT SQL이 2번 생성
2. 단일 테이블 전략
- 테이블을 하나만 사용한다.
- 구분 컬럼을 사용 해 자식 클래스를 구분한다.
- 조인을 사용하지 않아 일반적으로는 가장 빠르다.
- DiscriminatorColumn 을 꼭 설정 해야 한다.
- DiscriminatorValue를 지정하지 않으면 기본으로 엔티티 이름을 사용한다.
Character |
ID 이름 능력치 Jobs (자식 구분 컬럼) 힘(power) 지능(intellect) 행운(luck) |
<부모 클래스>
@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE) // JOIN전략에서 SINGLE_TABLE 전략으로 변경
@DiscriminatorColumn(name = "JOBS")
public abastract class Charecter { .... }
<자식 클래스> : 자식 클래스는 조인 전략과 동일하다.
@Entity
@DiscriminatorValue("A")
public class Warrior extends Character { .... }
단일 테이블의 장/단점
장점
- 조인이 필요 없어 쿼리가 단순하며 일반적으로는 조회 성능이 빠르다.
단점
- 다른 엔티티의 컬럼 값은 null이 들어 가므로 모두 null을 허용해야하며 불필요한 값이 들어가게 된다.
- 많은 타입이 존재하게 되는 경우 조회 성능이 떨어질 수 있다.
3. 구현 클래스 마다 테이블 전략
- 부모 테이블이 따로 존재하지 않고 각각의 테이블에 입력된다.
Warrior | Mage | Rogue |
ID 이름 능력치 Character_ID (PK, FK) 힘(power) |
ID 이름 능력치 Character_ID (PK, FK) 지능(intellect) |
ID 이름 능력치 Character_ID (PK, FK) 행운(luck) |
<부모 클래스>
@Entity
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS) // 전략 변경
//구분 컬럼 삭제
public abastract class Charecter { .... }
<자식 클래스> : 자식 클래스도 구분 값을 삭제한다.
@Entity
//구분 값 삭제
public class Warrior extends Character { .... }
구현 클래스마다 테이블 전략의 장/단점
장점
- 서브 타임을 구분해서 처리할 때 효과적이다.
- not null 제약조건 사용이 가능하다.
- 구분에 사용되는 어노테이션을 사용하지 않는다.
단점
- 여러 자식 테이블 함께 조회하기 어렵고 성능이 느리다( UNION )
*결론 : 추천하지 않는 전략이다.
Entity가 아닌 공통 로직 상속
@MappedSuperclass
- @Entity와 다르게 실제 테이블과는 매핑 되지않고 상속 목적으로만 사용한다.
- 생성날짜와 변경날짜에 대한 공통 컬럼
<부모 클래스>
@MappedSuperclass
public abstract class BaseEntity {
@CreatedDate
@Column(name = "regDate", updatable = false)
private LocalDateTime regDate;
@LastModifiedDate
@Column(name = "modDate", updatable = true)
private LocalDateTime modDate;
}
<자식 클래스>
@Entity
//
/** 매핑 정보 변경 시 사용하는 어노테이션
@AttributeOverride(name = "regDate", column = @Column(name = "REGIST_DATE"))
@AttributeOverrides({
@AttributeOverride(name = "regDate", column = @Column(name = "REGIST_DATE")),
@AttributeOverride(name = "modDate", column = @Column(name = "MODIFY_DATE"))
})
/**
public class Warrior extends BaseEntity { .... }
- 부모 클래스에는 Entity 어노테이션 대신 MappedSuperClass 어노테이션을 사용한다.
- 부모 클래스는 실제 테이블이 아니다.
- 자식 클래스는 부모클래스의 Column을 상속받는다.
- 매핑 정보를 변경하려면 AttributeOverride 어노테이션을 사용하여 변경한다.