티스토리 뷰

Spring Data JPA로 DB에 데이터를 조회하는 과정에서 이슈 하나가 발생하였는데 어떤 이슈인지 원인과 해결 방법은 무엇인지 말씀드리겠습니다.

 

이슈

JPQL이 아닌 직접 SQL을 작성 후 데이터를 조회하는 과정에서 Enum 타입의 파라미터가 정상적으로 바인딩이 되지 않는 이슈였습니다.

 

현상

테스트코드는 간단합니다. Member 객체를 생성 후 DB에 저장하고 type으로 다시 조회해 오는 코드입니다.

 

 

type으로 조회해 오는 코드는 다음과 같습니다. 이제 데이터를 조회해 보겠습니다.

 

 

insert는 정상적으로 실행되었는데 select에서는 예외가 발생합니다.

원인은 NullPointerException인데 jdbcMapping 이라는 변수가 null이라 그런 것 같습니다.

왜 NullPointerException이 발생하였는지 한번 확인해 보겠습니다.

 

원인

bindParameterValue 메서드가 호출되기 전에 guessBindType 메서드가 먼저 실행됩니다.

그리고 resolveParameterBindType 메서드를 통해 BindableType 타입의 값을 반환하는데요.

JdbcMapping 인터페이스의 서브 타입이 아닐 경우 null을 반환하게 되어있습니다.

이제 해당 메서드 내부를 들여다보겠습니다.

 

 

위에 빨간 박스로 테두리 친 영역을 보면 첫 번째는 resolveParameterBindType 메서드를 통해 값이 null이 아니면 반환하고 null일 경우 부모 클래스 타입을 얻어서 다시 한번 루프를 돕니다.

 

두 번째는 파라미터 타입이 Enum이 아니고 Serializable 인터페이스를 구현한 클래스일 경우 값을 반환하고 

그 외에는 null을 반환합니다.

 

즉, 파라미터 타입이 Enum일 경우 do while문에서 type 변수가 null이면 해당 메서드는 null을 반환하게 됩니다.

resolveParameterBindType 메서드 내부를 다시 한번 살펴보겠습니다.

 

 

MemberType으로 해당 메서드 내부를 실행하면 위 조건에 만족하여 basicType 변수를 반환합니다.

basicType 변수를 확인해 보니 null이 저장되어 있고 isEnum 메서드를 호출하면 true가 반환되어 해당 메서드는 null을 반환하게 됩니다.

 

 

이제 MemberType Enum의 super class인 java.lang.Enum을 얻습니다.

여기서 java.lang.Enum은 Enum이 아니고 abstract class입니다. basicType은 null이어도 isEnum 메서드가 false를 반환하면서 메서드 흐름은 중단되지 않습니다. 더 밑으로 가보겠습니다.

 

 

javaType 변수가 null이 아닐 경우 JdbcType 타입의 값을 반환하게끔 작성되어 있는데 값을 확인해 보니 null입니다.

그리고 밑의 조건엔 만족하지 않기 때문에 그대로 null을 반환합니다.

 

 

정리하자면

1. AbstractJdbcParamter#guessBindType 메서드로 Bind 대상이 되는 타입을 조회
2. SessionFactoryImpl#resolveParamterBindType 메서드에서 전달된 파라미터로 Bind 타입을 조회 
(여기서 전달된 파라미터는 예제코드에서 실행된 메서드의 매개변수를 의미)

3. (2) 작업에서 null이 반환될 경우 해당 타입의 부모 타입을 얻고 다시 한번 (2) 작업을 반복
4. (2~3)을 반복해서 처리해도 null이 반환될 경우 타입을 체크하여 Enum일 경우 null을 반환


해결

Spring Data JPA의 @Query 어노테이션에서 SpEL을 지원합니다. 그래서 위 사진처럼 SpEL을 이용해서 처리할 수 있습니다.

 

이번에는 정상적으로 Enum 타입의 파라미터가 바인딩 되었고 select 쿼리도 의도한 대로 잘 실행되었습니다.

 

 

만약에 Enum을 하나가 아닌 여러개를 넣고싶으면 아래와 같이 진행하시면 됩니다.

먼저 파라미터로 List<Enum<?>>을 받는 정적 메서드를 생성 후 Stream으로 순회하면서 name을 반환하게끔 합니다.

 

 

SpEL은 Lambda Expression이 불가능하므로 위처럼 위와 같이 작성하시면 됩니다. 위 표현식은 SpEL의 문법입니다.

 

 

정상적으로 IN 쿼리가 실행된 걸 확인하실 수 있습니다.

 

참고

https://spring.io/blog/2014/07/15/spel-support-in-spring-data-jpa-query-definitions

 

SpEL support in Spring Data JPA @Query definitions

Spring Data JPA allows manually defining the query to be executed by a repository method using the @Query annotation. Unfortunately parameter binding in JPQL is quite limited only allowing you to set a value and providing some type conversion. The latest S

spring.io

 

728x90
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG
more
«   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
글 보관함