[DB] 테이블을 설계하자! -3

1. 조인전략

엔터티 각각을 테이블로 만들고 자식 테이블이 부모 테이블의 기본키를 받아서 pk+fk로 사용한다.

*주의 :객체는 타입으로 구분할 수 있지만 테이블은 타입의 개념이 없어서
Dtype 컬럼과 같이 타입을 구분하는 컬럼이 있어야함

@Entity
@Inheritance(strategy = InheritanceType.JOINED)
@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 {

    private String artist;

}

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

    private String director;
    private String actor;

}

@Entity
@DiscriminatorValue("B")
@PrimaryKeyJoinColumn(name = "BOOK_ID")
public class Book extends Item {

    private String author;
    private String isbn;

}

@inheritance

상속 매핑은 부모 클래스에 @Inheritance를 사용해야 한다. 그리고 매핑 전략을 지정하기 위해 InheritanceType.JOINED를 사용하였다.

@DiscriminatorColumn

부모 클래스에 구분 컬럼을 지정한다. 이 컬럼으로 저장된 자식 테이블을 구분할 수 있으며, 기본 값은 "DTYPE"이다.

@DiscriminatorValue

엔티티를 저장할 때 구분 컬럼에 입력할 값을 지정한다. 기본값은 엔티티의 이름이다.

@PrimaryKeyJoinColumn

name 속성을 통해 자식 테이블의 기본 키 컬럼명을 지정한다. 기본값은 부모 테이블의 ID 컬럼명이다.

장점

-테이블이 정규화된다.
-외래 키 참조 무결성 제약 조건을 활용할 수 있다.
-저장 공간을 효율적으로 사용한다.

단점

-조회할 때 조인이 많이 사용되므로 잘못 이용하면 성능이 저하될 여지가 있다.
-조회 쿼리가 복잡하다.
-데이터를 등록할 때 INSERT SQL을 두 번 실행한다.

2. 단일 테이블

단일 테이블은 테이블 하나에 모든 데이터를 다 넣는다.
구분 컬럼을 통해 어떤 자식 데이터가 저장되었는지 구분하며 조회시 조인 하지 않는다.

*주의:자식 엔티티가 매핑한 칼럼은 모두 NULL

'

@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 {

    private String artist;

}

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

    private String director;
    private String actor;

}

@Entity
@DiscriminatorValue("B")
@PrimaryKeyJoinColumn(name = "BOOK_ID")
public class Book extends Item {

    private String author;
    private String isbn;

}

장점

-조인이 필요 없으므로 일반적으로 조회 성능이 빠르다.
-조회 쿼리가 단순하다.

단점

-자식 엔티티가 매핑한 컬럼이 모두 null을 허용해야 한다.
-단일 테이블에 모든 것을 저장하므로 테이블이 커질 수 있다. 그러므로 상 황에 따라서는 조회 성능이 오히려 느려질 수 있다.

3.구현 클래스마다 테이블 생성

자식 엔티티마다 테이블을 만들고 각각 필요한 컬럼을 모두 만든다.

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

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

    private String name;
    private int price;

}

@Entity
public class Album extends Item {

    private String artist;

}

public class Movie extends Item {

    private String director;
    private String actor;

}

@Entity
public class Book extends Item {

    private String author;
    private String isbn;

}

구분 컬럼을 사용하지 않으며 일반적으로 잘 사용안한다.

장점

-서브 타입을 구분해서 처리할 때 효과적이다.
-not null 제약 조건을 사용할 수 있다.

단점

-여러 자식 테이블을 함께 조회할 때 성능이 느리다.
-SQL에 UNION을 사용해야 한다.
-위 예제 코드에서 em.find(Item.class) 같은 것을 호출하는 순간 자식 테이블을 전부 다 조회해야 한다.
-자식 테이블을 통합해서 쿼리하기 어렵다.

@MappedSuperclass

부모 클래스는 테이블과 매핑하지 않고 부모 클래스를 상속 받는 자식 클래스에게 매핑 정보만 제공하고 싶으면 해당 어노테이션을 사용한다. @MappedSuperclass는 추상 클래스와 비슷한데, @Entity는 실제 테이블과 매핑되지만 @MappedSuperclass는 실제 테이블과 매핑되지는 않는다. 단순히 매핑 정보를 위로 뽑아서 재사용할 목적으로 사용한다.

@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;

}

@MappedSuperclass의 특징

테이블과 매핑되지 않고 자식 클레스에 엔티티의 매핑 정보를 상속하기 위해 사용한다.
해당 어노테이션이 붙은 클래스는 엔티티가 아니다.
추상 클래스로 만드는 것을 권장한다.

좋은 웹페이지 즐겨찾기