Hello

[jpa+hibernate] 두 개의 Enum으로 JPA상속 Hibernate @DiscriminatorFormula 활용 본문

java

[jpa+hibernate] 두 개의 Enum으로 JPA상속 Hibernate @DiscriminatorFormula 활용

nari0_0 2025. 11. 7. 14:20
728x90

데이터베이스에 두 개의 Enum 컬럼이 존재하고 어플리케이션에서는 Enum 값을 조합하여 클래스 단위로 구분해 처리한 내용을 JPA 상속 매핑 전략, Hibernate의 @DiscriminatorFormula 활용, 조합형 Enum을 엔티티에서 사용 방법으로 나눠 정리하고자 합니다.

1. JPA 상속 매핑 전략 

JPA는 상속 구조를 테이블에 매핑하기 위해 세 가지 전략을 제공함.

전략 특징
SINGLE_TABLE 모든 상속 엔티티를 하나의 테이블에 저장함
JOINED 부모/자식 테이블 분리 후 조인함
TABLE_PER_CLASS 자식별 독립 테이블 사용함

 

- @DiscriminatorColumn과 @DiscriminatorValue

구분컬럼(discriminator column)을 통해 어떤 엔티티 타입인지 판별

ex) 아래 예시 코드에서 dtype은 JPA가 직접 관리하는 컬럼이다.

new Movie()를 저장하면 JPA가 dtype='MOVIE'로 값을 자동으로 넣어준다.(쓰기, 읽기 모두 JPA가 처리해준다.)

@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "dtype")
public abstract class Item {
    @Id @GeneratedValue
    private Long id;
}

@Entity
@DiscriminatorValue("MOVIE")
public class Movie extends Item {
    private String director;
}

2. Hibernate 확장 : @DiscriminatorFormula

한 컬럼으로 타입을 구분하기 어려울 때 사용합니다.

ex) 가격, 장르 조합으로 엔티티 구분이 필요한 경우 이 때! Hibernate가 만든 확장 기능인 @DiscriminatorFormula를 사용하면 쉽게 처리 가능합니다.

public enum Genre {
    ACTION, COMEDY;//장르
}

public enum PriceLevel {
    LOW, HIGH;//가격 수준
}

@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorFormula("concat(genre, '_', price_level)")
public abstract class Item {

    @Id
    @GeneratedValue
    private Long id;

    @Enumerated(EnumType.STRING)
    @Column(name = "genre")
    private Genre genre;

    @Enumerated(EnumType.STRING)
    @Column(name = "price_level")
    private PriceLevel priceLevel;

    private String title;

    // getters, setters
}

@Entity
@DiscriminatorValue("ACTION_LOW")
public class ActionLowMovie extends Item {
    private String director;
    // getters, setters
}
@Entity
@DiscriminatorValue("COMEDY_HIGH")
public class ComedyHighMovie extends Item {
    private String actor;
    // getters, setters
}

 

Hibernate 내부쿼리

select id, genre, price_level, concat(genre, '_', price_level) as clazz_ from item;

clazz_ =  "ACTION_LOW" -> ActionLowMovie 매핑됩니다.

@DiscriminatorFormula는 읽기 전용 식이므로, JPA가 직접 값을 저장하지 않습니다. genre, price_level 컬럼을 개발자가 직접 세팅해야 합니다.

구분 역할
@DiscriminatorColumn 읽기 쓰기
@DiscriminatorFormula 읽기

3. 문자열 구분 규칙과 @DiscriminatorValue

Formula와 동일한 규직으로 작성해야 하며 구분자( _ )는 자유롭가 사용 가능합니다. 

4. 조합형 Enum활용

두 개 이상의 Enum을 조합해 어플리케이션에서 의미있는 하나의 타입으로 사용할 수 있습니다.

DB에는 genre, price_level 컬럼이 저장되고 엔티티를 조회할 때 자동으로 combinedType이 세팅 됩니다.

@Entity
public class Item {
	...
    @Transient
    public CombinedMovieType computeCombined() {
        this.combined = CombinedType.of(genre, priceLevel);
    }
}

public enum CombinedMovieType {
    ACTION_LOW(Genre.ACTION, PriceLevel.LOW),
    ACTION_HIGH(Genre.ACTION, PriceLevel.HIGH),
    COMEDY_LOW(Genre.COMEDY, PriceLevel.LOW),
    COMEDY_HIGH(Genre.COMEDY, PriceLevel.HIGH);

    private final Genre genre;
    private final PriceLevel priceLevel;

    CombinedMovieType(Genre genre, PriceLevel priceLevel) {
        this.genre = genre;
        this.priceLevel = priceLevel;
    }

    public static CombinedMovieType of(Genre g, PriceLevel p) {
        for (CombinedMovieType type : values()) {
            if (type.genre == g && type.priceLevel == p) {
                return type;
            }
        }
        throw new IllegalArgumentException("No matching CombinedMovieType");
    }
}

 

정리

상황 권장 사용
단일 컬럼으로 구분 가능 @DiscriminatorColumn
여러 컬럼 조합으로 구분 @DiscriminatorFormula
Enum조합 표현 @Transient

 

JPA 표준은 단일 구분 컬럼(@DiscriminatorColumn)만 지원

Hibernate는 이를 확장 해 여러 컬럼을 조합한 식(@DiscriminatorFormula)으로 타입 구분을 지원합니다.(읽기)

단순 구분 조합은 상속 하지 않고 두 Enum을 갖는 새로운 Enum을 만들어 @Transient로 처리할 수 있습니다.

728x90