JPA 고급 매핑

상속 관계 매핑

RDB에는 객체지향 언어에서 다루는 상속 개념이 존재하지 않는다. 그나마 슈퍼타입, 서브타입 관계라는 모델링 기법이 객체의 상속 개념과 가장 유사하다.

조인 전략

  • 엔티티 각각을 모두 테이블로 생성하고, 자식 테이블이 부모 테이블의 기본키를 받아 기본키 + 외래키로 사용하는 전략이다.
  • 테이블은 타입의 개념이 없어 타입을 구분하는 컬럼을 추가한다.

@Entity
@Inheritance(strategy = InheritanceType.JOINED)
@DiscriminatorColumn(name = "DTYPE")
public abstract class Item {
    @GeneratedValue @Id
    private Long id;
    
    ...
}

@Entity
@DiscriminatorValue("A")
@PrimaryKeyJoinColumn(name = "ALBUM_ID")
public class Album extends Item {
    private String artist;
    
    ...
}
애너테이션 선언위치 설명 기본값
@Inheritance(strategy=InheritanceType.XXX) 부모 상속 매핑 전략을 지정한다. 속성값에는 JOINED, SINGLE_TABLE, TABLE_PER_CLASS 이 있다. SINGLE_TABLE
@DiscriminatorColumn(name="XXX") 부모 구분 컬럼을 지정한다. 이 컬럼을 통해 저장된 자식 테이블을 구분한다. 하이버네이트의 조인 전략에서는 @DiscriminatorColumn을 선언하지 않으면 DTYPE 컬럼이 생성되지 않는다. DTYPE
@DiscriminatorValue("XXX") 자식 엔티티를 저장할 때 슈퍼타입의 구분 컬럼에 저장할 값을 지정한다. 클래스명
@PrimaryKeyJoinColumn(name = "XXX") 자식 상속 매핑에서 자식 테이블의 기본키 컬럼명을 변경할 때 사용한다. 부모 테이블의 ID 컬럼명
조인 전략의 장점
  • 테이블이 정규화 됨.
  • 외래키 참조 무결성 제약조건을 활용.
  • 저장공간을 효율적으로 사용.
조인 전략의 단점
  • 조회시 조인을 많이 사용하므로 성능 저하.
  • 조회 쿼리가 복잡해짐.
  • 데이터 저장시 INSERT SQL을 2번 호출.

단일 테이블 전략

테이블을 하나만 사용하여 구분 컬럼(DTYPE)으로 자식 데이터를 구분한다. 그래서 엔티티에 따라 사용하는 컬럼이 다르다.

@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "DTYPE")
public abstract class Item {

    @Id
    @GeneratedValue
    @Column(name = "ITEM_ID")
    private Long id;
    ...
}

@Entity
@DiscriminatorValue("A")
public class Album extends Item {
    ...
}

...
단일 테이블 전략의 장점
  • 조인이 필요 없어 조회 성능이 빠름.
  • 조회 쿼리가 단순함.
단일 테이블 전략의 단점
  • 자식 엔티티가 매핑한 컬럼은 모두 null이 허용됨.
  • 단일 테이블에 모두 저장하기 때문에 테이블이 커질 수 있음. 그래서 상황에 따라 조회 성능이 느려짐.

구현 클래스마다 테이블 전략

자식 엔티티마다 테이블을 생성한다. 이 전략은 사용을 지양한다.

@Entity
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
@DiscriminatorColumn(name = "DTYPE")
public abstract class Item {

    @Id
    @GeneratedValue
    @Column(name = "ITEM_ID")
    private Long id;
    
    ...
}

@Entity
public class Album extends Item {
    ...
}

...
구현 클래스마다 테이블 전략의 장점
  • 서브 타입을 명확하게 구분해서 처리할 때 효과적임.
  • not null 제약조건 사용 가능.
구현 클래스마다 테이블 전략의 단점
  • 여러 자식 테이블을 함께 조회할 때 성능이 느림.(UNION SQL 필요)
  • 자식 테이블을 통합하기 어려움.

@MappedSuperclass

  • 테이블과 매핑하지 않고 부모 클래스를 상속받는 자식 클래스에게 매핑 정보만 제공한다.
  • 추상 클래스와 비슷하며 실제 테이블과 매핑되지 않는다.
  • @MappedSuperclass를 선언한 클래스는 엔티티가 아니므로 조회, 검색이 불가능 하다. 즉, em.find(), JPQL을 사용할 수 없다.
  • 직접 생성해서 사용할 일이 없으므로 추상 클래스로 구현하는 것을 권장한다.
  • 일반적으로 공통 매핑 정보가 필요할 때 사용된다. (등록일자, 등록자 등)

@MappedSuperclass
public abstract class BaseEntity {
    @Id
    @GeneratedValue
    private Long id;
    
    private String name;
    
    ...
}

@Entity
public class Member extends BaseEntity {
    //ID 상속
    //NAME 상속
    private String email;
}

@Entity
public class Seller extends BaseEntity {
    //ID 상속
    //NAME 상속
    private String shopName;
}

상속된 매핑 정보 재정의

부모로 물려받은 매핑 정보를 재정의할 경우 @AttributeOverrides, @AttributeOverride를 사용할 수 있다.

@Entity
@AttibuteOverrides({
    @AttibuteOverride(name = "id", column = @Column(name = "MEMBER_ID")),
    @AttibuteOverride(name = "name", column = @Column(name = "MEMBER_NAME")),
})
public class Member extends BaseEntity {
    ...
}

엔티티는 @Entity 또는 @MappedSuperclass가 선언된 클래스만 상속 받을 수 있다.

Categories:

Updated:

Comments