티스토리 뷰

사건의 발단

  • 예전에 사용하던 MySQL의 데이터를 구해서 새로 작업할 DB에 이식했다.
  • 그 중에 지역 정보를 담고 있던 area라는 테이블의 Entity를 만들다고 JPA의 find관련 메서드로 데이터를 가져오는 부분에서 문제가 발생했다.

area의 스키마

  • 다른 건 필요 없고 문제가 생긴 칼럼만 확인해 보자.

Entity 작성

@Entity
@Table(name = "area")
public class AreaEntity {

    ...

    @Enumerated(value = EnumType.String)
    @Column(name = "kind")
    private Kind kind;

    public enum Kind {
        TOURISM, LEISURE, CULTURE;
    }
}
  • 기존에 늘 작성하던대로 다음과 같은 형태로 작성했다.
  • 500에러가 발생해서 콘솔 로그를 확인해 보니 다음과 같은 오류가 나타났다.
    • nested exception is java.lang.IllegalArgumentException: No enum constant com.study.kokkokapi.persistence.entity.AreaEntity.Kind.culture
  • 알아보니 area 테이블의 kind 칼럼은 enum('tourism', 'leisure', 'culture')이고 Entity에서 사용하는 enum 값은 TOURISM, LEISURE, CULTURE다.
    • 한마디로 대소문자가 구분이 되어서 발생하는 에러였다.
  • 열심히 구글링을 해보니 Converter라는 것을 사용하면 이런 변환을 쉽게 할 수 있다고 한다.

Converter 적용


import javax.persistence.AttributeConverter;
import javax.persistence.Converter;
import javax.persistence.Convert;

@Converter
class KindConverter implements AttributeConverter<AreaEntity.Kind, String> {
    @Override
    public String convertToDatabaseColumn(AreaEntity.Kind attribute) {
        return attribute.toDbValue();
    }

    @Override
    public AreaEntity.Kind convertToEntityAttribute(String dbData) {
        return AreaEntity.Kind.from(dbData);
    }
}

@Entity
@Table(name = "area")
@Convert(converter = KindConverter.class, attributeName = "kind")
public class AreaEntity {

    ...

    @Column(name = "kind")
    private Kind kind;

    public enum Kind {
        TOURISM, LEISURE, CULTURE;

        public String toDbValue() {
            return this.name().toLowerCase();
        }

        public static Kind from(String dbData) {
            return Kind.valueOf(dbData.toUpperCase());
        }
    }
}
  • KindConverter를 살펴보면 javax.persistence.AttributeConverter<X, Y> 인터페이스를 구현하고 있다.
    • 여기서 오버라이딩한 convertToDatabaseColumn 메서드는 Entity의 enum을 DB의 enum으로 변환하는 메서드다.
    • 다른 오버라이딩 메서드인 convertToEntityAttribute는 DB의 enum을 Entity의 enum으로 변환하는 메서드다.
  • 현재 DB의 enum이 소문자고 Entity의 enum이 대문자인 상황이다.
  • 하려고 했던 작어비 DB 데이터를 조회해서 화면에 전달해주는 작업이기 때문에 convertToEntityAttribute 메서드가 사용된다.
    • DB에서 가져온 enum 문자열을 toUpperCase() 메서드를 사용해서 대문자로 변환해서 Entity의 Kind enum으로 변환한다.
  • 위의 코드를 사용하면 정상적으로 데이터를 가져올 수 있다.

추가 사항

  1. 기존에 kind에 있던 @Enumerated(value = EnumType.String)을 그대로 붙이고 Converter를 사용한다면 소용이 없다.
    • 자세히 공부하지는 않아서 정확하지는 않지만 Converter는 가져온 데이터를 변환해서 매핑해주는 기능이라고 하면 @Enumerated(value = EnumType.String)은 가져올 때부터 매핑을 해서 가져오기 때문에 에러를 유발하는 것 같다.
    • 따라서 Converter를 사용할 때는 @Enumerated를 제거해야 한다.
  2. AreaEntity에서 @Convert(converter = KindConverter.class, attributeName = "kind")에서 attributeName 속성을 지정해주지 않으면 어떤 필드에서 Converter를 사용해야할지 모르기 때문에 실행에 실패한다.
  3. KindConverter에 선언된 @Converter라는 애노테이션을 살퍼보면 autoApply라는 속성이 존재하고 기본값은 false다.
    • 이 속성을 true로 지정하게 되면 글로벌하게 이 Converter가 적용된다.
    • 그렇기 때문에 AreaEntity 클래스에서 @Convert를 제거해도 동작한다.
  4. 사실 위에서 발생한 문제는 DB 스키마에서 enum의 항목을 대문자로 변경하면 손쉽게 해결되는 문제다.
    • Converter는 대소문자 변환 말고도 여러 곳에서 활용될 수 있기 때문에 정리해봤다.

References

'토이 프로젝트 > 충북 콕! 콕! (Renewal)' 카테고리의 다른 글

개발기  (7) 2020.08.18
기술 스택 변경  (0) 2020.08.15
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함