기초/JPA

[JPA] 객체간의 매핑

장동규 2022. 10. 11. 11:40

상속

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 어노테이션을 사용하여 변경한다.