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)
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으로 저장되어야 하나, 이제는 1로 저장이 되어서 다시 DB로부터 불러올 때 USER로 값이 변환되는 큰 문제가 생긴다.
@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";
}
}
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;
}