카테고리 없음

[JPA]상속관계맵핑

노트코드 2022. 1. 21. 02:02

조인전략

@Entity
@Inheritance(strategy=InheritanceType.JOINED) // join 전략을 사용
@DiscriminatorColumn(name = "DTYPE") // 부모 클래스에 구분 컬럼을 지정
public class Item{

    @Id @GenerateValue
    private Long id;

    private String name;

    private int price;
}

@Entity
@DiscriminatorValue(value = "A")
public class Album extends Item{
    private String artist;
}

@Entity
@DiscriminatorValue(value = "M")
public class Movie extends Item{
    private String director;
    private String actor;
}

@Entity
@DiscriminatorValue(value = "B")
@PrimaryKeyJoinColumn(name = "BOOK_ID")
public class Book extends Item{
    private String author
    private String isbn;
}
  • @Inheritance(strategy=InheritanceType.JOINED)
    상속 매핑은 부모 클래스에 @Inheritance를 사용, 그리고 매핑 전략 설정

  • @DiscriminatorColumn(name = "DTYPE")
    부모 클래스에 구분 컬럼을 지정, 이 컬럼으로 지정된 자식 테이블을 구분할 수 있다.

  • @DiscriminatorValue(value = "A")
    엔티티를 저장할 때 구분 컬럼에 입력할 값을 지정한다.

  • @PrimaryKeyJoinColumn(name = "BOOK_ID")
    Book 테이블의 ITEM_ID 기본 키 컬럼명을 BOOK_ID로 변경

이런 구조에서 데이터를 넣게되면 ITEM와 MOIVE테이블에 맞는 데이터가 들어가게 되고,데이터조회할때는 PK이자 FK인 ID로 join해서 가져오게된다.

장점

  • 테이블 정규화
  • 외래 키 참조 무결성 제약조건 활용가능
  • 저장공간 효율화

단점

  • 조회시 조인을 많이 사용, 성능 저하
  • 조회 쿼리가 복잡함
  • 데이터 저장시 Insert SQL 2번 호출

특징

  • JPA 표준 명세는 구분 컬럼을 사용하도록 하지만 하이버네이트를 포함한 몇몇 구현체는 구분 컬럼 없이도 동작한다.

단일 테이블 전략

@Inheritance(strategy=InheritanceType.SINGLE_TABLE)

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

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

    private String name;

    private int price;
}

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

}

@Entity
@DiscriminatorValue("M")
public class Movie extends Item {

}

@Entity
@DiscriminatorValue("B")
public class Book extends Item {

}

위의 코드를 실행하였을경우 위의 이미지 처럼 테이블이 생성됨, 즉 모든 필드가 한 테이블로 들어옴

장점

  • 쿼리할때 join이 발생하지 않는다.
  • 조회 쿼리가 단순함

단점

  • NULL을 허용해야함
  • 단일 테이블에 모든 것을 저장하므로 테이블이 커질 수있음, 상황에 따라서 조회 성능이 오히려 느려질 수 있다.

특징

  • 구분 컬럼을 꼭 사용해야 한다. 따라서 @DiscriminatorColumn을 꼭 설정해야 한다.
  • @DiscriminatorValue를 지정하지 않으면 기본으로 엔티티 이름을 사용한다.
    (Movie, Album, Book)

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

@Entity
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class ItemTablePerClass {

    @Id
    @GeneratedValue
    @Column
    private Long id;

    private String name;

    private int price;

}

@Entity
public class MovieTablePerClass extends ItemTablePerClass {

}
@Entity
public class BookTablePerClass extends ItemTablePerClass {
}
@Entity
public class AlbumTablePerClass extends ItemTablePerClass {

}

위 방식으로 만들면 ITEM테이블은 생성되지 않는다. 하지만 ITEM을 상속받은 각각의 테이블에는

위 그림처럼 해당 테이블에 필요한 컬럼이 전부 들어간다.

단점

  • 내가 찾고자 하는 데이터가 어느 테이블에 있는지 몰라서 모든 테이블을 전부 조회해서 union해야함
  • 새로운 타입이 추가될때 테이블을 너무 많이 수정 해야함

특징

  • 구분 컬럼을 사용하지 않는다.

@MappedSuperClass

부모 클래스는 테이블과 매핑하지 않고, 부모 클래스를 상속받는 자식 클래스에게 매핑 정보만 제공하고 싶을때 @MappedSuperClass를 사용하면된다

@MappedSuperclass
public class BaseEntity {
    @Id
    @GeneratedValue
    private Long id;

    private String name;
}

@Entity
public class Member extends BaseEntity {
    private String email; 
}
@Entity
public class Seller extends BaseEntity {
    private String shopName;
}

실행결과 Member 와 Seller 테이블에 Id와 name이 들어있는것을 확인할 수 있다.
또한 BaseEntity 테이블은 생성이 되지 않는다.

부모로 부터 물려받은 매핑 정보 재정의 하기

// 한개일때
@Entity
@AttributeOverride(name = "id", column = @Column(name = "MEMBER_ID"))
public class Member extends BaseEntity {
    private String email;
}

//2개이상일때
@Entity
@AttributeOverrides({@AttributeOverride(name = "id", column = @Column(name = "SELLER_ID")),
        @AttributeOverride(name = "name", column = @Column(name = "SELLER_NAME"))

})
public class Seller extends BaseEntity {
    private String shopName;
}

특징

  • 테이블과 매핑되지 않고 자식 클래스에 엔티티의 매핑 정보를 상속하기 위해 사용한다.
  • @MappedSuperclass로 지정한 클래스는 엔티티가 아니므로 em.find()나 JPQL에서 사용 불가
  • 이 클래스를 직접 생성해서 사용할 일은 거의 없으므로 추상 클래스로 만드는 것을 권장