티스토리 뷰

 

자바에서는 부모 클래스와 하위 클래스로 이루어진 상속 관계가 존재합니다. 그러나 RDB에서는 상속 관계가 존재하지 않습니다. 

RDB에서는 슈퍼 타입과 서브 타입 관계가 존재하는데, 이 모델링 기법이 상속 관계와 유사합니다.

 

ORM에서 슈퍼 타입과 서브 타입을 어떻게 다루는지 알아봅시다.


상속관계 매핑

ORM에서 슈퍼 타입과 서브 타입 관계를 다루기 위해 다음 3가지 전략이 존재합니다.

  • 조인 전략
  • 단일 테이블 전략
  • 구현 클래스마다 테이블 전략

이러한 전략을 사용하기 위해서는 @Inheritance 애너테이션을 사용해야 합니다. @Inheritance의 기본 전략은 단일 테이블 전략입니다.


조인 전략

Album, Movie, Book 엔티티는 id, name, price 필드가 공통적으로 사용됩니다. 따라서 Item이라는 엔티티를 만들어 공통되는 속성들을 저장해줍니다. Album, Movie, Book 엔티티는 Item 엔티티에 조인하여 다룰 수 있습니다. 부모 테이블의 PK를 자식 테이블이 상속 받아 이를 PK + FK 로 사용합니다.

 

+ 추가 정보

부모 테이블의 PK를 상속 받는 자식 테이블은 이를 PK이면서 FK로 사용한다. 이때 부모 테이블의 PK명을 그대로 사용하게 된다. 별도로 지정하기 위해서는 서브 엔티티(자식 테이블) @PrimaryKeyJoinColumn(name = "사용할 PK명") 을 사용하면 된다.

 

Item 엔티티 - 슈퍼 타입

@Inheritance(strategy = InheritanceType.JOINED) 애니테이션을 사용하여 조인 전략임을 명시해줍니다. 또한 DB에서 서브 타입을 구분하기 위하여 DTYPE 컬럼을 사용해야 합니다. 이때 @DiscriminatorColumn 애너테이션이 사용되는데, 이 애너테이션은 슈퍼 타입에서 서브 타입을 구분할 컬럼명을 지정해주면 됩니다. (기본값 "DTYPE")

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

    private String name;
    private int price;
    
    // Getter, Setter ..
}

Movie 엔티티 - 서브 타입

ORM에서는 RDB에서 슈퍼-서브 타입을 명시해주어야하기 때문에 상속(extends)를 사용할 수 있습니다. Movie 엔티티는 Item 엔티티와 슈퍼-서브 타입관계임을 알 수 있습니다. 슈퍼 타입에서는 DTYPE이 필요로 했다면 서브 타입에서는 DTYPE에 저장할 이름을 명시해주어야 합니다. 이때 @DiscriminatorValue 애너테이션이 사용됩니다. (기본값 "엔티티명")

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

    private String director;
    private String actor;
    
    // Getter, Setter..
}

Album과 Book 엔티티는 Movie 엔티티와 동일하므로 생략하겠습니다.

Movie 엔티티 저장

기생충이라는 영화를 DB에 저장해봅시다. 

Movie movie = new Movie();
movie.setName("기생충");
movie.setDirector("봉준호");
movie.setActor("송강호");
movie.setPrice(20000);
em.persist(movie);

Movie 엔티티를 영속성 컨텍스트에 올렸는데 Item 엔티티와 Movie 엔티티 두 곳에 Insert 쿼리문이 나갔습니다. Item 엔티티에는 왜 Insert 쿼리문이 나간 걸까요? 이는 Movie 테이블(서브 타입)에 있는 일부 속성(name, price)이 Item 테이블(슈퍼 타입)이 가지고 있는 공통 속성이기 때문입니다. 따라서 공통 속성에 해당하는 필드는 Item 테이블에 Insert 쿼리문이 나가고 나머지 속성은 Movie 테이블에 Insert 쿼리문이 나가게 됩니다.

Movie 엔티티 조회

DB에 저장된 Movie 엔티티를 조회해봅시다.

Movie findMovie = em.find(Movie.class, movie.getId());

Item 엔티티와 조인을 하여 조회함을 알 수 있습니다. Item 테이블이 가진 공통 속성과 Movie 테이블이 가진 나머지 속성을 모두 조회해야하기 때문입니다.


단일 테이블 전략

단일 테이블 전략은 RDB에서 슈퍼-서브 타입 관계를 하나의 테이블에 모두 저장하여 관리합니다. 

Item 테이블의 공통 속성과 Album, Movie, Book 테이블의 나머지 속성들이 하나의 테이블에 모두 관리됩니다.

Item 엔티티

단일 테이블 전략은 @Inheritance(strategy = InheritanceType.SINGLE_TABLE) 애너테이션을 사용합니다. 이때 DTYPE을 생략해도 되는데, 하나의 테이블로 서브 타입을 구분해야하기 때문에 반드시 DTYPE을 필요로 하게 됩니다. 따라서 자동으로 DTYPE 컬럼이 생성됩니다.

@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
//@DiscriminatorColumn(name = "DTYPE")
public class Item {
    @Id
    @GeneratedValue
    @Column(name = "ITEM_ID")
    private Long id;

    private String name;
    private int price;
    
    // Getter, Setter..
}

Movie 엔티티

기존과 엔티티와 동일합니다.

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

    private String director;
    private String actor;
    
    // Getter, Setter..
}

Movie 엔티티 저장

기존과 동일한 코드입니다.

Movie movie = new Movie();
movie.setName("기생충");
movie.setDirector("봉준호");
movie.setActor("송강호");
movie.setPrice(20000);
em.persist(movie);

이번에는 Insert 쿼리가 Item 테이블에 나갔습니다. 위에서 말했듯이 하나의 테이블에서 슈퍼-서브 타입을 모두 다루기 때문에 Item 테이블에 쿼리문이 나가게 됩니다. 또한 조인 전략과 달리 Insert 쿼리문이 1번만 발생합니다.

Movie 엔티티 조회

Movie findMovie = em.find(Movie.class, movie.getId());

하나의 테이블에서 조회하면 되기 때문에 조인이 발생하지 않습니다.

하나의 테이블에서 Movie 테이블의 속성뿐만 아니라 Book, Album 테이블의 속성도 다루기 때문에 Movie 테이블과 관련 없는 속성은 null이 저장되게 됩니다.


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

공통되는 속성을 갖는 슈퍼 타입을 생성하지 않고 모든 서브 타입의 테이블에 공통 속성이 저장됩니다.

Item 엔티티

단일 테이블 전략은 @Inheritance(strategy = InheritanceType.TABLE_PER_CLASS) 애너테이션을 사용합니다. 이때 DTYPE 속성이 필요 없는데, 그 이유는 공통 속성을 각자 엔티티에서 다루기 때문에 이를 구분할 DTYPE 자체가 필요 없게 됩니다.

@Entity
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public class Item {
    @Id
    @GeneratedValue
    @Column(name = "ITEM_ID")
    private Long id;

    private String name;
    private int price;
    
    // Getter, Setter..
}

Movie 엔티티

기존과 동일합니다.

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

    private String director;
    private String actor;
    
    // Getter, Setter..
}

Movie 엔티티 저장

Movie movie = new Movie();
movie.setName("기생충");
movie.setDirector("봉준호");
movie.setActor("송강호");
movie.setPrice(20000);
em.persist(movie);

Movie 테이블에 모든 값이 저장됩니다.

Movie 엔티티 조회

Movie findMovie = em.find(Movie.class, movie.getId());

Movie 테이블을 대상으로 조회합니다.

Movie 테이블에 모든 속성이 존재합니다.


결론

Total
Today
Yesterday
최근에 올라온 글
«   2024/11   »
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30