[인프런 김영한] JPA - 상속관계 매핑

2021. 2. 9. 15:18프로그래밍 언어/Spring Framework

[인프런 김영한] JPA - 상속관계 매핑


해당 글은 인프런 김영한강사님의 영상을 보고 정리한 글입니다.

Spring Boot, Spring Data JPA를 사용해 실습하였습니다.

김영한 인프런 : www.inflearn.com/users/@yh

 

인프런 - 김영한의 강의들을 만나보세요.

우아한형제들 개발 팀장 (전: 카카오, SK플래닛) 저서: 자바 ORM 표준 JPA 프로그래밍

www.inflearn.com


▣ Goal

1. JPA 기본 전략

2. 조인 테이블 전략

3. 단일 테이블 전략

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


▣ 상속관계 매핑

* 관계형 데이터베이스는 상속관계가 없다.

* 상속관계 매핑 : 객체의 상속과 구조와 DB의 슈퍼타입, 서브타입(객체 상속과 유사) 관계를 매핑

 

* 슈퍼타입 서브타입 논리 모델을 실제 물리 모델로 구현하는 방법

 - 각각의 테이블로 변환 -> 조인전략

 - 통합 테이블로 변환 -> 단일 테이블 전략

 - 서브타입 테이블로 변환 - 구현 클래스마다 테이블 전략

 


▣ JPA 기본 조인전략

 

 

Item

@Entity
public class Item {

    @Id
    @GeneratedValue
    private Long id;

    private String name;
    private int price;

}

 

Album, Movie, Book

@Entity
public class Album extends Item{

    private String artist;
}
@Entity
public class Movie extends Item{
    private String director;
    private String actor;
}
@Entity
public class Book extends Item{

    private String author;
    private String isbn;

}

 

실행 결과

create table item (
  dtype varchar(31) not null,
  id bigint not null,
  name varchar(255),
  price integer not null,
  artist varchar(255),
  author varchar(255),
  isbn varchar(255),
  actor varchar(255),
  director varchar(255),
  primary key (id)
)

| JPA 기본전략은 하나의 테이블에 모두 넣는 단일 테이블 전략을 사용하고 있습니다.

 

 


 

 

▣ JPA 조인전략 - JOINED (조인전략)

※ 테이블 설계시 기본으로 선택하며, 정말 단순하다면 단일 테이블 전략 사용.

Item 수정

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

    @Id
    @GeneratedValue
    private Long id;

    private String name;
    private int price;

}

| Item Table에 @Inheritance(strategy = InheritanceType.JOINED) 전략을 사용했습니다.

IngeritanceType에 JOINED를 사용하게 되면 단일 테이블 전략이 아닌, 조인 테이블 전략을 사용하게 됩니다.

 

JOINED : 조인전략

SINGLE_TABLE : 단일 테이블 전략

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

 

 

장점

 - 데이터가 정규화가 되어 있다.

 - 외래키 참조 무결성 제약조건을 사용 가능하다.

 - 저장공간이 효율화 됩니다.

 

단점

 - 조회시 조인을 많이 사용합니다.

 

 

 

Insert Data

Movie movie = new Movie();
movie.setDirector("디렉터");
movie.setActor("엑터");
movie.setName("영화이름");
movie.setPrice(10000);

movieRepository.save(movie);


// 조회
System.out.println("=======================");
movieRepository.findById(movie.getId());

 

 

 

실행결과

Hibernate:
    create table album (
        id bigint not null,
        artist varchar(255),
        primary key (id)
    )
Hibernate: 
    create table book (
        id bigint not null,
        author varchar(255),
        isbn varchar(255),
        primary key (id)
    )
Hibernate: 
    create table item (
        id bigint not null,
        name varchar(255),
        price integer not null,
        primary key (id)
    )
Hibernate: 
    create table movie (
        id bigint not null,
        actor varchar(255),
        director varchar(255),
        primary key (id)
    )
    
Hibernate: 
    alter table album 
       add constraint FKrl4nl1yn7tatob2buih6y9qws 
       foreign key (id) 
       references item
Hibernate: 
    alter table book 
       add constraint FKqk00l5u7w76kq5n45m9h5t5rj 
       foreign key (id) 
       references item
Hibernate: 
    alter table movie 
       add constraint FK77hfitoaq24bt17vl2307651e 
       foreign key (id) 
       references item
       
Hibernate: 
    insert 
    into
        item
        (name, price, id) 
    values
        (?, ?, ?)
Hibernate: 
    insert 
    into
        movie
        (actor, director, id) 
    values
        (?, ?, ?)
        
//조회
Hibernate: 
    select
        movie0_.id as id1_2_0_,
        movie0_1_.name as name2_2_0_,
        movie0_1_.price as price3_2_0_,
        movie0_.actor as actor1_3_0_,
        movie0_.director as director2_3_0_ 
    from
        movie movie0_ 
    inner join
        item movie0_1_ 
            on movie0_.id=movie0_1_.id 
    where
        movie0_.id=?

 

 

H2 database

SELECT * FROM ITEM;
ID  	NAME  	PRICE  
1	영화이름	10000

SELECT * FROM ALBUM;
ARTIST  	ID  
 
SELECT * FROM BOOK ;
AUTHOR  	ISBN  	ID  

SELECT * FROM MOVIE ;
ACTOR  	DIRECTOR  	ID  
엑터	  디렉터	    1

 

 

 


▣ Dtype

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

    private String name;
    private int price;

}

| @DiscriminatorColumn을 사용하게 되면 Dtype에 INSERT된 TABLE 명이 오게 된다.

 

 

실행결과

SELECT * FROM ITEM ;
DTYPE  	ID  	NAME  	PRICE  
Movie	1	영화이름	10000

SELECT * FROM MOVIE ;
ACTOR  	DIRECTOR  	ID  
엑터	  디렉터	    1

▣ JPA 조인전략 - SINGLE_TABLE(단일 테이블 전략)

| 하나의 테이블에서 DTYPE으로 구분합니다.

 

장점

 - 테이블이 하나만 생성이 되어 조회시 조인이 없으며 조회 성능이 빠릅니다.

 

단점

 - 자식 Entity가 매핑한 Column은 null을 허용해야 합니다.

 - 단일 테이블에 모든것을 저장하기 때문에 테이블이 커질 수 있습니다.

 

 

 

@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn
public class Item {

    @Id
    @GeneratedValue
    private Long id;

    private String name;
    private int price;

}

 

 

 

실행결과

SELECT * FROM ITEM;
DTYPE  	ID  	NAME  	PRICE  	ARTIST  AUTHOR  ISBN  	ACTOR  	DIRECTOR  
Movie	1	영화이름	10000	null	null	null	엑터	 디렉터

 

 


▣ JPA 조인전략 - TABLE_PER_CLASS(구현 클래스마다 테이블 전략)

※ 쓰면 안되는 전략

| Item 테이블을 없애고 Item에 있는 속성을 각각의 테이블의 속성에 넣는다.

 

장점

 - 서브 타입을 명확하게 구분해서 처리할 때 효과적

 - not null 제약조건을 사용 가능

 

 

단점

 - insert때는 문제가 없지만 만약 Item의 5번을 찾을 때 ALBUM, MOVIE, BOOK 테이블을 전부다 찾는다. (UNION)

 - 자식테이블을 통합해서 쿼리하기 어려움.

 

 

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

    @Id
    @GeneratedValue
    private Long id;

    private String name;
    private int price;

}

 

 

 

실행결과

Hibernate: 
    create table album (
       id bigint not null,
        name varchar(255),
        price integer not null,
        artist varchar(255),
        primary key (id)
    )
Hibernate: 
    create table book (
       id bigint not null,
        name varchar(255),
        price integer not null,
        author varchar(255),
        isbn varchar(255),
        primary key (id)
    )
Hibernate:
    create table movie (
       id bigint not null,
        name varchar(255),
        price integer not null,
        actor varchar(255),
        director varchar(255),
        primary key (id)
    )
    
    
    
    
SELECT * FROM MOVIE ;
ID  	NAME  	PRICE  	ACTOR  	DIRECTOR  
1    영화이름	10000	엑터	디렉터

SELECT * FROM ALBUM ;
ID  	NAME  	PRICE  	ARTIST  

SELECT * FROM BOOK ;
ID  	NAME  	PRICE  	AUTHOR  	ISBN