Enum Type을 더 효율적으로 사용하기

Kevin·2023년 10월 30일
0

JPA(Hibernate)

목록 보기
3/9
post-thumbnail

Enum을 사용한 내 코드

@Entity
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class Member {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id", nullable = false)
    private Long id;

    private String userName;

    private UserType userType;

    public static Member of(UserReqDTO userReqDTO) {
        return Member.builder()
                .userName(userReqDTO.getUserName())
                .userType(userReqDTO.getUserType())
                .build();
    }
}

먼저 JPA에서 Enum 타입을 다루는 방식을 살펴보기 위해서 먼저 @Enumerated 어노테이션에 대해서 살펴볼 필요가 있다.


먼저 @Enumerated 어노테이션은 두가지 기능을 지원한다.


@Enumerated(value = EnumType.ORDINAL)

@Enumerated(value = EnumType.ORDINAL)
private UserType userType;

첫째 ORDINAL, Enum의 선언된 순서를 Integer 값으로 변환하여 DB 컬럼에 저장한다.

// DB 컬럼은 numeric 타입이다.


즉 이 말은 Enum 내부에 선언된 상수들의 순서가 매우 중요하다는 말이기도 하다. 왜냐하면 DB에 저장된 값을 다시 가져올 때 Enum의 순서에 맞게 가져오기 때문이다.

이러한 특징 때문에 생기는 문제가 있다.

만약 아래와 같은 Enum 클래스가 있다고 해보자.

public enum UserType {
    ADMIN, USER
}

위 Enum들을 DB에 저장한다면 순서에 의해서 ADMIN은 0, USER은 1이 될 것이다.

이 때 USER와 ADMIN간의 순서를 어떠한 이유로 인해서 변경을 했을 때 DB에 저장된 값과 새롭게 저장되는 값들간에 차이가 생기게 된다.

  • 기존 값
    • ADMIN : 0
    • USER : 1
  • 변경된 값
    - ADMIN : 1
    - USER : 0

이렇게 차이가 생기면서 기존에 저장되어있던 ADMIN이 원래는 0으로 저장되어야 하나, 이제는 1로 저장이 되어서 다시 DB로부터 불러올 때 USER로 값이 변환되는 큰 문제가 생긴다.



@Enumerated(value = EnumType.STRING)

@Enumerated(value = EnumType.STRING)
private UserType userType;

둘째 STRING, Enum의 선언된 상수의 이름을 String 클래스 타입으로 변환하여 DB에 저장해준다.

//DB 컬럼은 String 타입이다.


2번의 경우에도 문제가 생기는데, 컬럼 자체를 String으로 저장하기에 DB의 공간 낭비가 생긴다는 것이다. 반면 1번의 경우 정수로 저장되기에 훨씬 저장공간이 절약된다.

그런데 맨 위 코드에서 @Enumerated 어노테이션을 사용하지 않았는데, 어떻게 JPA는 Enum 타입을 저장시킨 것일까?

→ JPA에서 Enum 타입 객체를 EnumType.*ORDINAL* 으로 기본으로 지정을 해준다.

→ @Enumerated의 인자 값을 설정하지 않으면 기본값으로 EnumType.*ORDINAL* 이 설정된다.


그러면 어떻게 해야 저장 공간 자체도 절약하면서, 인자 순서 문제를 해결할 수 있을까?

답은 Converter를 사용해서 값들을 직접 변환시켜주면된다.

궁금한게 Ordinary 해도 DB에 저장했다가 불러올 때 다시 문자열로 바뀌는 건 똑같은거 아닌가?
→ DB에 저장해뒀다가 다시 문자열로 바뀔 때 Ordinary는 순서가 바뀌면 심한 오류가 발생한다.


아래는 내가 직접 작성한 Converter 코드이다.

MemberConverter.class

import jakarta.persistence.AttributeConverter;

public class MemberConverter implements AttributeConverter<String, Integer> {

    // 엔티티 attribute 값을 DB에 저장할 값으로 변환하기 위한 메소드
    @Override
    public Integer convertToDatabaseColumn(String attr) {
        if ("ADMIN".equals(attr)) {
            return 1;
        } else if ("USER".equals(attr)) {
            return 2;
        }
        return 0;    }

    // DB에서 읽어온 값(INT)을 엔티티 attribute에 사용할 값으로 변환하기 위한 메소드
    @Override
    public String convertToEntityAttribute(Integer num) {
        if (num == 1) {
            return "ADMIN";
        } else if (num == 2) {
            return "USER";
        }
        return "ANONYMOUS";
    }
}
  • Converter 구현간에 AttributeConverter 를 구현해야하며, 각 변환할 타입을 인자로 넘겨주어야 한다.
  • convertToDatabaseColumn() 메서드는 엔티티 attribute 값을 DB에 저장할 값으로 변환하기 위한 메서드이다.
  • convertToEntityAttribute() 메서드는 DB에서 읽어온 값(INT)을 엔티티 attribute에 사용할 값으로 변환하기 위한 메서드이다.

이를 변환하고자 하는 엔티티의 필드에 @Convert 어노테이션으로 선언해준다.

@Convert(converter = MemberConverter.class)
private String userType;

또한 엔티티 필드 뿐만 아니라 엔티티 클래스 전역에 선언할 수도 있다.


@Entity
@NoArgsConstructor
@Getter
@Convert(converter = MemberConverter.class)
public class Member {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id", nullable = false)
    private Long id;

    private String userName;

    private String userType;

}
profile
Hello, World! \n

0개의 댓글