Spring Data JPA에서 Native Query에서 In 절에 Enum 넣기

 

JPA 쓰다보면 어려운 것도 아닌데 헷갈리는 것들이 있다..

복잡한 쿼리는 아닌데, paging 이랑 group by 동시에 써야 되서 native query 가 더 깔끔한 거 같아서 native query 사용했는데 조건에 enum 값에 있는 것들로 in 절에 넣어야됐다.

물론 그냥 따옴표 안에 'ENUM.CODE_A, ENUM.CODE_B' 이렇게 넣어주면 편하지만

이건 개발하는 입장에서 맘이 편하지 않으니까 패스..

Native Query 에서 Enum 사용시 문제

그래서 @Param("keys") List<EnumCode> keys 를 넣어

@Query(value = "" +
    "SELECT a.* " +
    "FROM user a " +
    "WHERE a.id = :userId AND a.remove = 0 " +
    "AND a.key IN (:keys) " +
    "GROUP BY a.create_at " +
    "ORDER BY a.create_at DESC",
    countQuery = "" +
    "SELECT COUNT(a.id) FROM user a " +
    "WHERE a.id = :userId AND a.remove = 0 " +
    "AND a.key IN (:keys) " +
    "GROUP BY a.create_at ",
    nativeQuery = true)
Page<User> getUserDataGroupByCreatedAtByKey(@Param(value = "userId") Long id, @Param("keys")List<EnumCode> keys, Pageable pageable);

이렇게 했는데 콘솔에 찍어보니

AND a.key IN ( 
	'ACED00057E72002F636F6D2E636865636B75702E776F726B73706163652E656E756D732E576F726B7370616365557365724C6F674B657900000000000000001200007872000E6A6176612E6C616E672E456E756D0000000000000000120000787074001D574F524B53504143455F555345525F4143544956455F555345525F4944'
  , 'ACED00057E72002F636F6D2E636865636B75702E776F726B73706163652E656E756D732E576F726B7370616365557365724C6F674B657900000000000000001200007872000E6A6176612E6C616E672E456E756D0000000000000000120000787074001F574F524B53504143455F555345525F494E4143544956455F555345525F4944' 
)

이렇게 찍혔다. 찾아보니

 

JPA에서 Enum 타입은 기본적으로 두 가지 방식(EnumType.ORDINAL 또는 EnumType.STRING)으로 데이터베이스에 저장될 수 있습니다. EnumType.ORDINAL은 Enum 값이 그 순서대로 데이터베이스에 숫자로 저장되는 것을 의미하고, EnumType.STRING은 Enum의 이름을 문자열로 저장하는 것을 의미합니다. 그러나, native query에서 이런 형태로 Enum 리스트를 사용할 경우, JPA의 자동 변환 기능에 의존할 수 없습니다.

즉, @Enumerated 어노테이션이 있더라도 native query에서는 이 설정이 적용되지 않아, Enum의 순서 값(ordinal)이 아닌 Enum의 이름을 원하는 경우에도 숫자 값으로 취급될 수 있습니다. 이는 JPA가 native query를 데이터베이스에 직접 전달하기 때문에, JPA의 Enum 처리 방식이 적용되지 않기 때문입니다.

 

그래서 고민했는데 생각해보니까 쉬운 문제였음 ㅎㅎ

 

해결 방안

List<String> keys = enumCodes.stream()
    .map(Enum::name)
    .collect(Collectors.toList());
@Query(value = "" +
    "SELECT a.* " +
    "FROM user a " +
    "WHERE a.id = :userId AND a.remove = 0 " +
    "AND a.key IN (:keys) " +
    "GROUP BY a.create_at " +
    "ORDER BY a.create_at DESC",
    countQuery = "" +
    "SELECT COUNT(a.id) FROM user a " +
    "WHERE a.id = :userId AND a.remove = 0 " +
    "AND a.key IN (:keys) " +
    "GROUP BY a.create_at ",
    nativeQuery = true)
Page<User> getUserDataGroupByCreatedAtByKey(@Param(value = "userId") Long id, @Param(value = "keys")List<String> keys, Pageable pageable);

완성 ^_^

이렇게 쉬운 걸..

String inClause = keys.stream()
                      .map(key -> "'" + key.name() + "'")
                      .collect(Collectors.joining(", ")); 

이런 식으로 삽질을 좀 해서 블로그에 정리해봤다.