프로젝트를 진행하다보면 엔티티의 필드 타입이 원시형이나 DB에서 지원하는 객체타입이 아닌 커스텀한 객체가 될 때가 있습니다. 그게 엔티티를 관리하거나 비지니스 코드를 작성하기 쉽기 때문에 그렇게 하는 경우가 많았습니다. 예를 들면 아래의 '날씨 데이터 타입'이 있습니다. enum 인스턴스나 커스텀 클래스를 통해 관련있는 정보를 묶어놓고 내부 메서드를 활용하면 훨씬 사용하기 편해서 여기저기 자주 사용했습니다.
@Getter
@AllArgsConstructor
public enum WeatherDataType {
TEMP("온도", "Temp", "C"),
RAIN("강수량", "Rain", "mm"),
;
private final String desc;
private final String code;
private final String unit;
...
}
문제는, 이 enum 값이 DB에 저장될 때는 enum 클래스에 정의된 순서인 Ordinal(1,2,3...) 값이 들어가게 된다는 것입니다. 이는 그 값 자체로도 의미가 없으며, enum 변수들의 나열 순서를 바꾸게 되면 각 숫자의 의미가 달라지는 등 문제가 있었습니다.
저는 이런 부분에 대해 Converter를 적극적으로 활용했습니다. 주로 code 값을 DB에 저장했고, DB에서 자바로 불러올 땐 code를 기반으로 다시 enum 혹은 참조형 자료형으로 가져오도록 했습니다.
사용하는 법은 간단한데, AttributeConverter를 구현한 Converter를 만들어줍니다. 이 때 AttributeConverter 옆에 붙은 제네릭은 Entity로 불러올 타입, DB에 저장할 타입 순서로 저장합니다. 아래 코드로 치자면 엔티티일땐 WeatherDataType이라는 객체에 담기게되고, DB에 저장될떈 String 타입으로 저장되게 됩니다.
저의 경우 enum을 이용해 맵핑하는게 편리했습니다.
@Converter
@Component
public class WeatherDataTypeConverter implements AttributeConverter<WeatherDataType, String> {
// 엔티티에서의 객체를 인자로 받아 데이터베이스에 저장할 내용을 return하면 됨
@Override
public String convertToDatabaseColumn(final WeatherDataType attribute) {
return attribute.getCode();
}
// 데이터베이스에 저장된 내용을 인자로 받아 엔티티 내의 필드로 변환할 내용을 return하면 됨
@Override
public WeatherDataType convertToEntityAttribute(final String dbData) {
return WeatherDataType.of(dbData);
}
}
그리고 만든 Converter를 엔티티의 필드에 붙여주면 됩니다. 그럼 해당 엔티티가 저장될때 Entity 필드에 선언된 클래스가 아닌 converter에 명시된대로 convert된 내용으로 저장되게 되고, DB에서 다시 불러올땐 객체 타입으로 convert됩니다.
@Convert(converter = WeatherDataTypeConverter.class)
@Column(name = "data_type", nullable = false, updatable = false)
private WeatherDataType weatherDataType;