java and sping

[Spring] JPA의 상속관계

코딩하는 공부방 2023. 5. 3. 00:29

인프런에 영한님 강의를 듣고 정리해본 블로그 게시물입니다.

 

JPA 연관관계(단방향/양방향 , 다중성)

인프런에 영한님 강의를 듣고 정리해본 블로그 게시물입니다. [Spring] JPA의 연관관계(단방향/양방향, 연관 관계의 주인) JPA에서 가장 중요한 것 JPA에서 가장 중요한 것을 뽑자면, "객체"와 데이터

nosechild.tistory.com

 

저번 블로그 포스팅에는 연관관계에서 다중에 대해서 자세히 다뤘는데요.
이번 블로그 포스팅은 JPA에서의 상속관계에 대해서 자세히 알아보겠습니다.


상속관계 매핑


<관계형 데이터베이스>

관계형 데이터베이스에는 기본적으로 상속 관계를 지원하지 않습니다.

JPA에서는 이러한 문제를 해결할 수 있는 3가지의 상속관계 매핑 방법을 지원합니다.

 

실습하기 전 아래의 간단한 테스트용 Entity를 2개(Item, Book)를 만들어줍니다.

주요 어노테이션


                                       @Inheritance(strategy = InheritanceType.XXX)

상속 관계 매핑 방법을 나타냅니다.

  • JOINED : 조인 전략
  • SINGLE_TABLE : 단일 테이블 전략
  • TABLE_PER_CLASS : 구현 클래스마다 테이블 전략
                                                @Discrimintaorcolumn(name="DTYPE")

부모 클래스에서 자식 클래스를 구분하는 컬럼의 이름을 설정합니다.

                                                         @DiscriminatorBalue("XXX")

자식 클래스를 식별하기 위해 부모의 DTYPE 컬럼에 저장될 값을 설정한다.
기본 값은 클래스 이름이다.

3가지 방법

각각 테이블로 전환 → 조인 전략


가장 정규화된 방법으로 구현하는 방식입니다.

슈퍼타입에서 @Inheritance(strategy = InheritanceType.JOINED) 통해 매핑전략을 조인 전략으로 지정했다.

@DiscriminatorColumn 애노테이션은 구분자 컬럼을 말한다. 조인 전략과 단일 테이블 전략에서 구분자 컬럼은 필수이다. 구분자 컬럼을 통해 어떤 서브타입의 데이터인지 구분해야 합니다. 한 테이블에 모든 컬럼을 저장하기 때문에, DTYPE 없이는 테이블을 판단할 수 없습니다.
구분자 컬럼의 이름의 기본값으로 "DTYPE"입니다. 사용자가 원하는 값으로 저장해 줄 수 있지만 특별한 이유가 없다면 기본값을 그대로 쓰는 것을 권장합니다.

JOINED 전략을 사용하면 테이블 정규화에 유리하고, FK 참조 시 무결성을 보장할 수 있으며, 저장 공간을 효율적으로 사용할 수 있습니다.

하지만 데이터가 분리되어 있어서 조회 시 JOIN을 많이 사용해야 하기 때문에 복잡하고 성능이 저하됩니다. 테이블이 분리되어 있어서 INSERT 쿼리를 2번을 호출해야 합니다.

단점으로 성능이 떨어질 수 있다고 했지만, 요즘 컴퓨터 성능이 워낙 좋아서 데이터가 엄청 많지 않은 이상 성능이 엄청 떨어지지 않습니다. 다음 살펴볼 단일 테이블 전략은 조인이 필요 없지만 null 데이터가 많아지기 때문에 상황에 따라 오히려 조인 전략보다 성능이 떨어질 수 있습니다.

SQL DDL문과 엔티티 설정

    
    create table book (
       author varchar(255),
        isbn varchar(255),
        id bigint not null,
        primary key (id)
    )

  
    create table item (
       dtype varchar(31) not null,
        id bigint not null,
        name varchar(255),
        price integer not null,
        primary key (id)
    )
    
  alter table book 
      add constraint FKqk00l5u7w76kq5n45m9h5t5rj 
      foreign key (id) 
      references item

 

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


조인 전략과 다르게 단일 테이블 전략은 이름 그대로 하나의 테이블에 모든 데이터를 몰아넣는 방식을 의미합니다.

조인 전략과 마찬가지로 서브 타입을 구분하기 위해서 구분자 컬럼이 필수로 적어야 합니다.

조인 전략에서 @Inheritance(strategy = InheritanceType.SINGLE_TABLE)을 통해 전략을 바꿔준 것 말고는 바뀐 코드가 없습니다. 이게 바로 JPA의 힘!! ORM 강력함입니다!! 상속관계 매핑 전략만 바꿔줘도 JPA가 알아서 엔티티를 알맞은 테이블에 매핑해 줍니다. ORM을 사용하지 않고 상속관계 매핑 전략을 바꿨다면 테이블 형태에 종속이 되어있는 SQL을 엄청나게 수정해야 합니다...

한 테이블에 데이터가 있어서 조인이 필요 없어서 조회 쿼리를 날리는 성능이 빠릅니다. JOIN을 할 필요 없어서 쿼리도 단순합니다. 단점으로는 자식 엔티티가 매핑한 칼럼이 null을 모두 허용해서 null 값이 많이 들어갑니다. 또한 단일 테이블에 모든 것을 저장하므로 테이블이 커질 수 있으므로 상황에 다라서 조회 성능이 오히려 조인 전략보다 늦어질 수 있습니다.

SQL DDL문과 엔티티 설정

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

 

구현 클래스마다 테이블 전략(쓰면 안 됨 X)


구현 클래스마다 테이블 전략은 슈퍼타입은 테이블로 매핑하지 않고 서브 타입만 테이블을 매핑하는 방법입니다. 이전 코드와 똑같이 @Inheritance@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)로 설정을 해주면 됩니다.

이걸 쓰면 조회할 때 문제가 발생을 합니다. 여러 자식 테이블을 함께 조회할 대 UNION을 사용해야 해서 성능이 느립니다.

장점이 있지만 단점이 커서 사용하지 않습니다. 컬럼에 NOT NULL 제약조건을 설정을 할 수 있습니다.

SQL DDL문과 엔티티 설정

 create table book (
       id bigint not null,
        name varchar(255),
        price integer not null,
        author varchar(255),
        isbn varchar(255),
        primary key (id)
    )