Light Blue Pointer
본문 바로가기
Developing/TIL(Develop)

Spring JPA 상속 관계

by Greedy 2024. 5. 10.

일반적으로 RDBMS에서는 상속 관계를 지원하지 않지만

JPA(Java Persistence API) 는 상속 관계를 지원해서 객체지향 모델링을 데이터베이스에 매핑해준다

일반적으로 JPA에서는 상속 관계를 세 가지 전략으로 매핑한다

단일 테이블 전략 (Single Table Strategy)

통합 테이블로 변환

모든 엔티티 클래스의 필드를 하나의 테이블에 매핑한다

부모 클래스와 자식 클래스의 모든 필드가 테이블의 열로 매핑되며, 부모 클래스와 자식 클래스를 구분하는 열(Discriminator Column)이 추가된다

장점

간단하고 효율적이다

조인이 필요하지 않아서 대체로 조회 성능이 좋다

조인이 필요하지 않아서 조회 쿼리가 단순하다

INSERT시 쿼리가 한 번만 호출된다

단점

모든 필드가 하나의 테이블에 있기 때문에 엔티티 클래스가 커질 수 있다

자식 엔티티에 매핑한 Column은 모두 null을 허용해야 한다

테이블의 Column이 많아지면 테이블의 크기가 커져 저장 공간이 낭비되고 조회 성능이 느려진다

구현 클래스마다 테이블 전략 (Class Per Table Strategy)

서브타입 테이블로 변환

모든 하위 클래스에 상위 클래스의 Column을 생성한다

많은 개발자가 심각한 단점으로 비추천한다

장점

서브타입을 명확하게 구분하여 처리할 수 있다

단일테이블 전략에서는 불가능한 not null 제약조건을 사용할 수 있다

상속 계층 구조를 데이터베이스에 정확하게 반영한다

단점

여러 자식 테이블을 함께 조회할때 UNION 연산이 발생하여 성능 저하가 있을 수 있다

자식테이블을 통합해서 쿼리하기 어렵고 쿼리 작성이 매우 복잡해진다

하위클래스가 추가되면 조회 코드를 변경해야 한다

조인 전략 (Joined Strategy)

각각 테이블로 변환

상위 클래스와 하위 클래스가 정의된 대로 만들어진다

각각의 엔티티 클래스마다 별도의 테이블을 생성한다

상위 클래스와 하위 클래스 간의 관계는 조인을 통해 유지된다

테이블이 구분되어있기 떄문에 데이터를 조회할 때 조인이 필요해서 조인 전략이라고 부른다

  • 상위 클래스에는 하위 클래스 구분을 위한 DTYPE이 생성된다
  • 자식 클래스에는 JOIN을 위한 ITEM_ID 칼럼이 생성된다

장점

상속 계층 구조를 정규화된 테이블로 표현한다

외래키 참조시 무결성 제약조건을 활용할 수 있다

필요없는 데이터는 저장하지 않아 저장 공간을 효율적으로 쓸 수 있다

단점

테이블이 나뉘어져있어 조회 시 조인이 필요하기 때문에 단일 테이블 전략에 비해 쿼리가 복잡해지고 성능이 저하된다

테이블이 나뉘어져있어 저장할 때 INSERT 쿼리를 두 번씩 호출해 단일테이블 전략에 비해 성능이 떨어진다

주의사항

상속 관계를 매핑할 때는 데이터베이스의 스키마와 객체지향 모델 간의 차이가 있을 수 있다는 것에 유의해야 한다

애플리케이션의 요구 사항과 데이터베이스의 특성을 고려하여 적절한 전략을 선택해야 한다

Annotations

@Inheritance(strategy = InheritanceType.XXX)

상속 전략을 지정하는데 사용된다

  • JOINED: 조인 전략
  • SINGLE_TABLE: 단일 테이블 전략
  • TABLE_PER_CLASS: 구현 클래스마다 테이블 전략

@DiscriminatorColumn(name="DTYPE")

단일 테이블 전략에서 부모 클래스와 자식 클래스를 식별하기 위한 구분 칼럼을 지정할 때 사용한다

@DiscriminatorValue("XXX")

단일 테이블 전략에서 각 자식 클래스의 구분 값을 지정할 때 사용한다

import javax.persistence.*;

@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "DTYPE")
public abstract class Vehicle {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String manufacturer;
    // other fields and methods
}

@Entity
@DiscriminatorValue("CAR")
public class Car extends Vehicle {
    private int numberOfDoors;
    // other fields and methods
}

@Entity
@DiscriminatorValue("BIKE")
public class Bike extends Vehicle {
    private int numberOfGears;
    // other fields and methods
}

Vehicle : 추상 클래스

Car , Bike : Vehicle 을 상속받는 두 개의 엔티티 클래스

Vehicle 클래스에는 @Inheritance@DiscriminatorColumn 어노테이션이 사용되어 단일 테이블 전략을 정의한다

CarBike 클래스에는 각각의 타입을 구분하기 위한 @DiscriminatorValue 어노테이션이 사용됨

@MappedSuperclass : 공통 속성이 존재할 때

이 어노테이션이 적용된 클래스는 테이블과 매핑되지 않는다

해당 클래스를 상속하는 서브클래스에서 공통 속성을 상속받아 사용한다

@MappedSuperclass의 특징

테이블과 매핑되지 않음

데이터베이스에 @MappedSuperclass로 지정된 클래스를 위한 테이블이 생성되지 않는다

공통 속성을 가짐

@MappedSuperclass로 지정된 클래스는 하위 클래스들이 공통적으로 사용할 수 있는 속성 및 매핑 정보를 제공한다

엔티티 상속 구조에 포함됨

@MappedSuperclass로 지정된 클래스는 엔티티 클래스가 아니므로, 테이블과 매핑되지 않지만 엔티티 상속 구조에 포함된다

상속 관계 매핑과 @MappedSuperclass의 차이점

매핑 방식

@MappedSuperclass는 단순히 속성을 상속하기 위한 것이므로, 테이블 간의 상속 관계를 표현하지 않는다. 반면에 상속 관계 매핑은 상속 구조를 테이블 간의 관계로 매핑한다.

테이블 생성 여부

@MappedSuperclass로 지정된 클래스는 테이블을 생성하지 않는다. 반면에 상속 관계 매핑에서는 상위 클래스와 하위 클래스 각각에 대한 별도의 테이블이 생성된다.

속성 상속

@MappedSuperclass는 단순히 속성을 상속하는 역할을 한다. 반면에 상속 관계 매핑에서는 하위 클래스가 상위 클래스의 속성을 상속받는 동시에 각 클래스의 특정 속성을 갖는 테이블이 생성된다.

복잡성

@MappedSuperclass는 단순한 상속 구조를 표현하는 반면, 상속 관계 매핑은 테이블 간의 관계를 정의하는 더 복잡한 매핑 방식이다

@MappedSuperclass는 단일 테이블에 속성을 공유하고 싶은 경우나 엔티티 클래스에서 공통으로 사용되는 매핑 정보를 정의할 때 유용하다

반면 상속 관계 매핑은 엔티티 간의 상속 구조를 테이블 간의 관계로 매핑할 때 사용되며, 더 복잡한 데이터 모델을 구현할 때 유용하다

@Entity 애노테이션을 통해 엔티티가된 클래스는 또 다른 엔티티 클래스나 @MappedSuperclass로 지정한 클래스만 상속할 수 있다

'Developing > TIL(Develop)' 카테고리의 다른 글

JPA 사용할때 쿼리가 복잡해지는 경우의 해결방안  (0) 2024.05.15
Spring mvc 패턴  (0) 2024.05.14
Spring Transactional  (0) 2024.05.09
Spring AOP  (0) 2024.05.08
Spring IoC Container, Bean  (0) 2024.05.06