JPA nativeQuery interface 리턴
당근 마켓 클론코딩중 거리 계산을 위해 ST_Distance_Sphere()를 사용하기로 했다.
ST_Distance_Sphere()는 jqpl에서는 동작하지 않으므로 nativeQuery를 사용해야 했다.
뭐 평소에 사용하던 jqpl문에서 실제 쿼리문으로만 약간만 변경 한 뒤, nativeQuery 옵션만 true를 넣어주면 실행될 꺼라 생각했다.
하지만 오늘도 여지없이 오류가 발생.. native쿼리 구현 후 dto로 리턴받으려고 하니 문제가 발생하였다.
아차.. jpql에서 사용하는 방법인 new 패키지.dto생성자 전략을 사용하니 당연히 문제가 될 수 밖에 없었다.. 허허
이후에 검색해 보니 nativeQuery에서는 인터페이스로 가져오는 방법이 존재하였다.
따라서 아래와 같이 구현!
@Query(value = "SELECT t.town_id, t.name, t.longitude, t.latitude, ST_Distance_Sphere(POINT(:longitude, :latitude), "
+ "POINT(t.longitude, t.latitude)) as distance from town t where ST_Distance_Sphere(POINT( "
+ ":longitude, :latitude), POINT(t.longitude, t.latitude)) <= "
+ ":distanceLevel ORDER BY distance",nativeQuery = true)
List<TownResultInterface> findTowns(@Param("longitude")BigDecimal longitude,@Param("latitude") BigDecimal latitude,@Param("distanceLevel") int distanceLevel);
public interface TownResultInterface {
Long getTownId();
String getName();
BigDecimal getLatitude();
BigDecimal getLongitude();
BigDecimal getDistance();
}
하지만 이후에도 Id값에서 null값이 나오는 문제가 발생하였다..
그래서 원리도 알아볼겸 디버깅을 해보았다.
원리는 이랬다..
우리가 만든 인터페이스는 aop를 통해 proxy 객체로 만들어지고,
위의 이미지와 같이 tupleBackedMap에 키값과 실제 value값이 입력된다.
우리는 이렇게 만들어진 데이터 값을 인터페이스에서 만들어 놓은 getter 메소드를 통해 접근 가능했던 것이다.
이제 돌아와서, '왜 getTownId()값이 null이었는지'로 되돌아가 보자..
위 이미지를 보면, townId의 key값이 'town_id'로 되어 있다.
그렇다.. 나는 사실 @RequestBody등의 리플랙션을 떠올리며 get메소드에서 필드값을 추측할 줄 알았다..
하지만 여기서는 get메소드가 아니라 select문의 컬럼값이 키값의 기준이 되어서 데이터가 저장되는 원리였다!
따라서 아래와 같이 변경해 주었다.
@Query(value =
"SELECT t.town_id as townId, t.name, t.longitude, t.latitude, ST_Distance_Sphere(POINT(:longitude, :latitude), "
+ "POINT(t.longitude, t.latitude)) as distance from town t where ST_Distance_Sphere(POINT( "
+ ":longitude, :latitude), POINT(t.longitude, t.latitude)) <= "
+ ":distanceLevel ORDER BY distance", nativeQuery = true)
List<TownResultInterface> findTowns(@Param("longitude") BigDecimal longitude,
@Param("latitude") BigDecimal latitude, @Param("distanceLevel") int distanceLevel);
town_id 값에 별칭으로 as townId를 추가하여 주었더니. 아래와 같이 잘 동작하는 것을 확인할 수 있었다!
id값의 key값도 townId로 잘 저장된 것을 확인 할 수 있다.