Home [Spring JPA] JPA 상속관계 매핑
Post
Cancel

[Spring JPA] JPA 상속관계 매핑

DB에서의 상속 관계

우리는 JPA를 사용해서 ‘객체지향적’으로 Entity를 관리하고 사용합니다. 그래서 Entity도 객체지향적으로 사용하고싶습니다. 하지만 관계형 DB는 상속 관계를 지원하지 않습니다. 객체지향에서의 상속을 Entity에 적용해서 사용할 수는 없을까요?

JPA에서의 상속 관계

이때 사용하는 것이 상속관계 매핑입니다. 위에서 언급했듯이 관계형 DB는 상속 관계를 지원하지 않습니다. 그러면 JPA는 어떻게 Entity의 상속 관계를 DB에 표현할까요?

우선 JPA에서 상속 관계를 사용하기 위해서는 상위 Entity에 @Inheritance, 하위 Entity는 상위 Entity를 상속해주어야 합니다.

@Inheritance를 사용할 때는 상속을 어떻게 DB에 구현할 건지에 대한 전략도 설정해주어야 합니다.

1
2
// 예시
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)

만약 설정하지 않으면 SINGLE_TABLE을 기본 값으로 사용합니다.

JPA가 DB에 Entity 상속을 구현하는 전략은 3가지가 있습니다.

조인 전략

조인 전략은 상위 테이블과 하위 테이블을 모두 생성한 뒤에, 사용할 때 조인해서 사용하는 전략입니다. strategy에 IngeritanceType.JOINED를 설정해서 사용할 수 있습니다.

Join-Strategy-ERD

위의 클래스 관계를 JPA에서 조인 전략을 활용하여 아래의 방식으로 DB에 Table을 만들어 저장합니다.

공통되는 속성은 모두 Item Table에 저장됩니다. 그리고 공통된 부분을 제외한 나머지 속성들을 모두 각자 table에 저장합니다.

상위 Table에 DTYPE이라는 것이 있습니다. 이건 상위 table에 저장된 데이터가 어떤 데이터인지 구분하기 위해 있는 칼럼입니다. 예를 들어 Item에 저장된 어떤 데이터의 DTYPE이 Album이라면, 이 데이터는 album정보를 저장한 데이터라는 것을 알 수 있습니다.

하이버네이트의 JOINED전략에서는 기본적으로 DTYPE이 생기지 않습니다. 그래서 DTYPE이 필요하다면 아래의 @DiscriminatorColumn을 사용해야합니다. DTYPE을 넣어주는 편이 명확하므로 가능하면 넣어주는 것이 좋습니다.

칼럼명 DTYPE를 바꾸고 싶다면 @DiscriminatorColumn(name=”DTYPE2”)와 같이 상위 Entity에 추가로 annotation을 붙여서 이름을 변경할 수 있습니다.

DTYPE에는 기본적으로 Entity의 이름이 들어갑니다. 위의 사진에서는 Album, Movie, Book이 들어갈 수 있습니다. 만약 이 값을 바꾸고 싶다면, @DiscriminatorValue(“Albums”)와 같이 하위 Entity에 annotation을 붙여 변경할 수 있습니다.

장점

  • 이 방법은 가장 정규화가 잘 되어있는 방식입니다. 데이터 중복이 최소화된 방법이죠.
  • 데이터 저장 공간을 효율적으로 사용할 수 있습니다.

단점

  • 매번 Join해서 조회하므로 성능이 다소 느려질 수 있습니다.
  • 데이터를 등록할 때 Insert가 2번 실행됩니다.
  • 조회 쿼리가 복잡합니다.

대부분의 일반적인 상황에서는 이 전략을 사용하면 됩니다.

단일 테이블 전략

단일 테이블 전략은 상위 테이블에 하위 테이블들의 모든 속성을 넣어 1개의 테이블로 관리하는 전략입니다. strategy에 IngeritanceType.SINGLE_TABLE를 설정해서 사용할 수 있습니다. 물론 이 전략이 기본 전략이므로 굳이 annotation에 명시하지 않아도 되긴합니다.

Single-Tagle-Strategy-ERD

Item 테이블에 Album, Movie, Book의 모든 속성을 포함시켜 Item 1개 테이블로 관리합니다.

이 전략에서는 DTYPE이 반드시 필요하기 때문에 DiscriminatorColumn을 선언하지 않아도 자동으로 DTYPE이 생깁니다.

장점

  • 조회할 때 조인이 필요 없으므로 성능이 좋습니다.
  • 데이터 등록시 insert 쿼리도 1번만 수행됩니다.

단점

  • 데이터가 다소 비효율적으로 저장됩니다. (NULL이 많이 사용됨)
  • 자식 엔티디의 칼럼들은 모두 Nullable로 등록됩니다.
  • 테이블이 커져서 오히려 조회 속도가 느려질 수도 있습니다.

서비스의 규모가 작아서 굳이 조인 전략이 필요 없다고 판단되면, 이 전략을 사용하면 됩니다.

자식(구현) 클래스마다 테이블 생성 전략

이 전략은 상위 테이블의 칼럼들을 모두 하위 테이블에 포함시켜, 하위 테이블로만 운용하는 전략입니다. strategy에 IngeritanceType.TABLE_PER_CLASS를 설정해서 사용할 수 있습니다.

Table-Per-Class-Strategy-ERD

장점

  • not null을 사용할 수 있습니다.
  • 서브 타입을 구분해서 사용할 수 있습니다.

단점

  • 여러 개의 자식 테이블을 함께 조회하면 union연산이 발생하므로 성능이 느려집니다.
  • 자식 테이블을 같이 포함해서 쿼리를 작성하기 어렵습니다.

이 전략은 사용하지 않는 것이 좋습니다. 각 자식 테이블들을 포함한 쿼리를 짜기 매우 어렵고 복잡해지기 때문입니다.

참고

  1. 김영한, 『자바 ORM 표준 JPA 프로그래밍 - 기본편』, 인프런
  2. https://ict-nroo.tistory.com/128
This post is licensed under CC BY 4.0 by the author.