JPA 고급 매핑 - 상속관계 매핑

2022. 7. 3. 18:37·공부/JPA
반응형

출처 : 자바 ORM 표준 JPA 프로그래밍 인프런 강의

 

JPA 게시글은 대부분 인프런의 김영한님의 강의인 '자바 ORM 표준 JPA 프로그래밍' 기반으로 내용을 정리했습니다.

 

상속 관계 매핑

  • 관계형 데이터베이스에는 상속 관계라는 개념이 존재하지 않는다.
  • 대신 슈퍼타입 서브타입 관계라는 모델링 기법이 객체 상속과 그나마 유사하다.
  • 상속 관계를 매핑 : 객체의 상속 구조와 DB의 슈퍼타입 서브타입 관계를 매핑

 

슈퍼타입 서브타입 논리 모델 -> 물리 모델로 구현

조인 전략

각각의 테이블로 만들어 JOIN으로 데이터를 뽑아오는 방식이다. ITEM이라는 상위 테이블을 만들고, ALBUM에 관한 데이터를 가져오고 싶으면 ITEM과 ALBUM을 JOIN하여 데이터를 가져온다.

  • 장점
    • 테이블 정규화가 가장 잘 되어있다.
    • 외래키 참조 무결성 제약조건 활용이 가능하다.
    • 저장공간이 효율적이다.
  • 단점
    • 조회 시 조인을 많이 사용하게 되어 성능 저하로 이어진다.
    • 조회 쿼리가 복잡하다.
    • 데이터 저장 시 INSERT SQL가 두 번 생성된다.

 

  • 객체 클래스 생성
// Item.class

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

    @Id @GeneratedValue
    private Long id;

    private String name;
    private int price;

    public Long getId() {
        return id;
    }
    ...getter/setter 생략
}
//Movie.class
@Entity
public class Movie extends Item{

    private String director;
    private String actor;
    
    ...getter/setter 생략
}
//Book.class
@Entity
public class Book extends Item {

    private String author;
    private String isbn;
    
    ...getter/setter 생략
}
//Album.class
@Entity
public class Album  extends Item{

    private String artist;
    
    ...getter/setter 생략
}

 

부모 클래스에 '@Inheritance(strategy = InheritanceType.JOINED)' 애노테이션을 주면 조인 전략으로 매핑된다. 

// 출력 로그
Hibernate: 
    
    create table Item (
       id bigint not null,
        name varchar(255),
        price integer not null,
        primary key (id)
    )
    
Hibernate: 
    
    create table Movie (
       actor varchar(255),
        director varchar(255),
        id bigint not null,
        primary key (id)
    )
    
Hibernate: 
    
    create table Album (
       artist varchar(255),
        id bigint not null,
        primary key (id)
    )
    
Hibernate: 
    
    create table Book (
       author varchar(255),
        isbn varchar(255),
        id bigint not null,
        primary key (id)
    )

 

  • DiscriminatorColumn

Item 테이블에는 자세히보면 DTYPE이 존재한다. DTYPE을 생성하려면 DiscriminatorColumn 애노테이션으로 설정해주면 된다.

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

... 생략


// 출력 로그
create table Item (
   DTYPE varchar(31) not null, // DTYPE 추가
    id bigint not null,
    name varchar(255),
    price integer not null,
    primary key (id)
)

DiscriminatorColumn 애노테이션을 설정해주면 INSERT SQL이 실행될 때 적절한 값을 알아서 잘 넣어준다. 참고로 기본값은 DTYPE이므로 그냥 @DiscriminatorColumn만 적어줘도 된다.

// 실행 클래스에서 Movie 관련된 INSERT SQL 생성 시
/* insert hellojpa.Movie
    */ insert 
    into
        Item
        (name, price, DTYPE, id) 
    values
        (?, ?, 'Movie', ?)

 

  • DiscriminatorValue

구분 컬럼에 입력할 값을 지정한다. 이 애노테이션을 지정하지 않을 시 기본값은 엔티티 이름이다.

// Movie.class
@Entity
@DiscriminatorValue("A") // DTYPE에 A로 저장된다.
public class Movie extends Item{
... 생략

 

  • 조인 전략은 em.persist()할 때 INSERT SQL 두 번 실행, em.find() 할 때 SELECT 문에서 JOIN이 이루어진다.
// 실행 클래스

...생략

Movie movie = new Movie();
movie.setDirector("who");
movie.setActor("are");
movie.setName("you");
movie.setPrice(10000);

em.persist(movie);

em.flush();
em.clear();

Movie findMovie = em.find(Movie.class, movie.getId()); // SELECT JOIN
System.out.println("findMove = " + findMovie);


...생략

// 출력 로그
//INSERT
Hibernate: 
    /* insert hellojpa.Movie
        */ insert 
        into
            Item
            (name, price, DTYPE, id) 
        values
            (?, ?, 'Movie', ?)
Hibernate: 
    /* insert hellojpa.Movie
        */ insert 
        into
            Movie
            (actor, director, id) 
        values
            (?, ?, ?)


// SELECT
Hibernate: 
    select
        movie0_.id as id2_2_0_,
        movie0_1_.name as name3_2_0_,
        movie0_1_.price as price4_2_0_,
        movie0_.actor as actor1_5_0_,
        movie0_.director as director2_5_0_ 
    from
        Movie movie0_ 
    inner join
        Item movie0_1_ 
            on movie0_.id=movie0_1_.id 
    where
        movie0_.id=?
findMove = hellojpa.Movie@39aa45a1

 

 

단일 테이블 전략

단일 테이블 전략은 그냥 Album, Movie, Book 테이블에 존재하는 모든 컬럼을 ITEM 테이블에 다 집어 넣는 방법이다.

  • 장점
    • 조인이 필요없으므로 조회 성능이 빠르다.
    • 조회 쿼리가 단순하다.
  • 단점
    • 자식 엔티티가 매핑한 컬럼은 모두 null을 허용해야한다.
      •  Album에 관련한 데이터를 저장할 경우 작곡가만 필요하고 나머지 감독, 배우, 작가, ISBN은 필요가 없으므로 null 허용을 줄 수 밖에 없다.
    • 단일 테이블에 모든 것을 저장하므로 테이블이 커질 수 있다. 상황에 따라서 오히려 조회 성능이 느려질 수 있다.

 

  • 단일 테이블 전략 구현

조인 전략에서 작성했던 Item 클래스 코드 한 줄만 아래와 같이 변경하면 된다.

@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
// 출력 로그
Hibernate: 
    
    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)
    )

 

DB에는 이렇게 저장된다.

 

  • @DiscriminatorColumn이 없어도 DTYPE이 알아서 들어간다.
//Item.class
@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
//@DiscriminatorColumn
public class Item {

...생략


// 출력 로그
Hibernate: 
    
    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)
    )

 

 

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

구현 클래스마다 테이블을 생성하는 전략이다. 즉 ITEM 테이블을 없애고 Albnm, Movie, Book 테이블에 ITEM 속성을 각각 집어넣어 중복을 감안한다.

  • 이 전략은 DB 설계자와 ORM 전문가 둘 다 추천하지 않는다.
  • 장점
    • 서브 타입을 명확하게 구분해서 처리할 때 효과적이다.
    • not null 제약 조건을 사용할 수 있다.
  • 단점
    • 여러 자식 테이블과 함께 조회할 때 성능이 느리다.(UNION SQL 필요)
    • 자식 테이블을 통합해서 쿼리하기 어렵다.

 

  • 구현 클래스 구현

Item 클래스의 Inheritance 애노테이션을 수정하고 Item 클래스를 추상 클래스로 변경한다. 추상 클래스가 아닐 시 Item의 테이블까지 같이 만들어진다. 즉 Item을 상속과 상관없이 독단적으로 사용할 수 있게 된다. (원래는 처음부터 추상 클래스로 만들었어야함이 맞다.)

@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class Item { // abstract 추가
... 생략


// 출력 로그
    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)
    )

Item 테이블은 생성되지 않았다.

 

* 위의 세가지 중 어떤 방식으로 DB를 구성하든 JPA에서 객체는 매핑을 할 수 있다.

 

반응형
저작자표시 비영리 변경금지 (새창열림)
'공부/JPA' 카테고리의 다른 글
  • JPA 프록시와 연관관계 - 프록시
  • JPA 고급 매핑 - Mapped Superclass 매핑 정보 상속
  • JPA 다양한 연관관계 매핑 - 다대일, 일대다, 일대일, 다대다
  • JPA 연관관계 매핑 - 양방향 연관관계와 연관관계의 주인
데부한
데부한
어차피 할 거면 긍정적으로 하고 싶은 개발자
    반응형
  • 데부한
    동동이개발바닥
    데부한
  • 전체
    오늘
    어제
    • 분류 전체보기 (307)
      • 방통대 컴퓨터과학과 (27)
        • 잡담 (9)
        • 3학년1학기 (17)
      • 프로젝트 및 컨퍼런스 회고 (1)
        • 프로젝트 (4)
        • 한이음 프로젝트 (0)
        • 회고 (3)
      • 공부 (165)
        • Spring (37)
        • JPA (71)
        • 인프런 워밍업 클럽_BE (10)
        • Java (6)
        • React.js (27)
        • 넥사크로 (11)
        • 기타 (3)
      • 알고리즘 (85)
        • 알고리즘 유형 (10)
        • 알고리즘 풀이 (57)
        • SQL 풀이 (18)
      • 에러 해결 (13)
      • 잡담 (7)
        • 국비교육 (2)
        • 구매후기 (5)
        • 진짜 잡담 (0)
  • 블로그 메뉴

    • Github
    • Linkedin
    • 홈
    • 방명록
    • 글쓰기
    • 관리
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    전자정부프레임워크
    코딩테스트
    자바스크립트
    JPA
    QueryDSL
    기출문제
    IT
    인프런
    egov
    oracle
    RESTful
    Java
    react
    방통대
    스프링부트
    프론트엔드
    springboot
    Spring
    에러해결
    백준
    개발자
    알고리즘
    SQL
    프로그래머스
    운영체제
    토이프로젝트
    넥사크로
    SpringBoot를 이용한 RESTful Web Service 개발
    토비의스프링부트
    MSA
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
데부한
JPA 고급 매핑 - 상속관계 매핑
상단으로

티스토리툴바