프로젝트에서 회원 상태나 성별을 Enum으로 관리하고 있다. Enum으로 관리한 값을 DB에 어떻게 매핑하여 저장할 것인가에 대해서 찾아보고 적용한 내용이다.
먼저, 성별 쪽 Enum은 다음과 같다.
public enum MemberGender {
MALE("M"),
FEMALE("F");
private final String gender;
MemberGender(String gender) {
this.gender = gender;
}
public String getGender() {
return gender;
}
}
먼저, Enum의 값, 즉 GenderEnum에선 gender
의 값으로 GenderEnum을 구하는 메서드가 필요하다.
수동으로 추가하려고 하다 보니, Java에서 지원해주는 메서드가 있어서 그걸 사용하기로 했다.
Java의 Enum.valueOf()
출처: https://docs.oracle.com/javase/8/docs/api/java/lang/Enum.html
그리고 Entity는 간략화하여, 다음과 같다.
@Entity
@Table(name = "user")
public class MemberEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private MemberGender gender;
// ...
}
처음에는 String gender
로 선언 후, Enum의 값을 반환하는 메서드를 추가하여 필요할 때마다 호출해 넣어줄 예정이었다.
그런데, Enum gender
로 선언하고 Enum과 매핑시켜주는 방법이 있다고 해서 알아봤다.
Enum을 DB에 매핑하는 방법은 총 2가지가 있다.
첫 번째 방법은 @Enumerated
어노테이션을 사용하는 것으로, 이는 JPA 2.1 이전에 사용되었다고 한다.
// 사용법
@Enumerated(EnumType)
Enum타입 변수명;
위의 사용법을 보면 알겠지만, @Enumerated
은 괄호를 열고 어떤 값이 저장될 지 EnumType
을 설정할 수 있다.
EnumType
에는 총 2가지 타입이 있다.
괄호 안의 내용을 적지 않았을 경우, 이 타입이 default 값으로 설정된다고 한다.
MemberGender
의 경우엔 MALE이 맨 처음 선언되었으므로 0, FEMALE이 그 다음이므로 1이 매핑된다.@Entity
@Table(name = "user")
public class MemberEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Enumerated(EnumType.ORDINAL)
private MemberGender gender;
// ...
}
MemberGender
의 경우엔 “MALE”과 “FEMALE”이라는 이름이 그대로 저장된다.@Entity
@Table(name = "user")
public class MemberEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Enumerated(EnumType.STRING)
private MemberGender gender;
// ...
}
@Enumerated
는 Enum의 순서나 이름이 변경되면 데이터를 잘못 읽거나 아예 읽지 못하는 문제가 발생한다.
따라서, 이러한 문제를 해결하기 위해 JPA 2.1 이후에 나온 것이 @Converter
이다.
@Converter
는 DB의 값과 Entity의 값을 매핑시켜주는 Converter 클래스를 구현하는 방식을 말한다.
Converter 클래스에는 총 2가지 메서드가 필요하다.
Converter 클래스는 AttributeConverter
라는 인터페이스를 구현하여 만든다.
해당 인터페이스에는 2개의 메서드가 있다.
AttributeConverter<MemberGender, String>
처럼 제네릭으로 타입을 선언한다.@Converter
public class GenderAttributeConverter implements AttributeConverter<MemberGender, String> {
@Override
public String convertToDatabaseColumn(MemberGender attribute) {
// Enum의 gender 값 반환
return attribute.getGender();
}
@Override
public MemberGender convertToEntityAttribute(String dbData) {
// valueOf() 메서드로, 해당 gender와 일치하는 MemberGender를 반환
return MemberGender.valueOf(dbData);
}
}
💡 @Converter는 필수인가?
예제를 찾아봤을 때, 해당 어노테이션을 적지 않은 코드도 있었다. 쓰거나 쓰지 않거나 똑같이 동작하는 것처럼 보여서 왜 사용하는 것인지 찾아봤다.
공식 문서에 따르면, 해당 클래스가 Converter 임을 명시해주기 위해 기입하는 것으로 보인다.
물론, autoApply 값을 설정해서 자동으로 매핑시켜주는 기능도 있다고 하는데, 이건 그리 사용할 거 같지 않다.
문서: https://docs.oracle.com/javaee/7/api/javax/persistence/Converter.html
이렇게 만든 Converter는 다음과 같이 적용시키면 된다.
@Entity
@Table(name = "user")
public class MemberEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Convert(converter = GenderAttributeConverter.class)
private MemberGender gender;
// ...
}
Enum 뿐만이 아니라 패스워드처럼 암호화되어 저장되어야 하는 경우도 Converter로 분리해서 사용할 수 있다.
정리하고 보니 별 내용이 없지만, 막연히 JPA가 Enum을 자동 매핑시켜주는 기능을 제공하더라 하고 기억하는 것보단 이렇게 조금이라도 찾아서 적용해보는 게 실력에 도움이 될 거 같다.
[Spring] DB 코드값과 Enum의 매핑 방법 @Enumerated vs @Convert