간단한 기능만을 구현하여 JPA 에서 제공하는 메서드로만 해결을 하다가, 조금 복잡한 쿼리를 실행해야 하는 경우가 생겼다.
특정 좌표를 기준으로 지정한 바운더리 내에 존재하는 데이터 목록 조회
욜로가 서비스의 메인 기능이다. 현재 좌표를 서버로 보내주면 내가 원하는 범위 내의 주변 러닝크루 정보를 얻어낸다. 이를 구현하기 위해서 MySQL 의 함수인 ST_Distance_Sphere
가 필요했다.
페이징 처리 필요
내 주변 범위를 크게 잡으면 데이터 개수에 따라 많은 시간이 소요될 수 있으므로 페이징 처리를 하여 순차적으로 API 를 호출하기 위해 필요하다.
이전에는 조금 복잡한 쿼리가 필요하더라도 JPQL 정도로 해결 가능했으나, MySQL 의 함수를 사용하기에는 어려웠다. 그래서 찾아낸 방법이 실제 SQL 문을 그대로 사용하는 NativeQuery 였다.
public interface UserRepository extends JpaRepository<User, Long> {
@Query(value = "SELECT * FROM USERS WHERE EMAIL_ADDRESS = ?1", nativeQuery = true)
User findByEmailAddress(String emailAddress);
}
JPQL 과 같이 @Query
어노테이션을 사용하면서 nativeQuery 속성을 true 로 설정하면 된다.
그렇다면 페이징 처리는 어떻게 포함할 수 있을까? 공식문서에 아주 친절하게 나와있다.
public interface UserRepository extends JpaRepository<User, Long> {
@Query(value = "SELECT * FROM USERS WHERE LASTNAME = ?1",
countQuery = "SELECT count(*) FROM USERS WHERE LASTNAME = ?1",
nativeQuery = true)
Page<User> findByLastname(String lastname, Pageable pageable);
}
countQuery 의 속성을 추가하고 Pageable 객체를 같이 받아오면 된다.
@Query(
value = "SELECT *\\n"
+ "FROM running_crew\\n"
+ "WHERE ST_Distance_Sphere(POINT(?2, ?1), departure) <= ?3 AND archived = true\\n"
+ ";",
countQuery = "SELECT count(id)\\n"
+ "FROM running_crew\\n"
+ "WHERE ST_Distance_Sphere(POINT(?2, ?1), departure) <= ?3 AND archived = true\\n"
+ ";",
nativeQuery = true
)
Page<RunningCrew> findNearby(final Double latitude, final Double longitude, final int radius, final Pageable pageable);
참고) SELECT 문을 사용할 때 성능상의 문제로 *
는 잘 사용하지 않지만, NativeQuery 를 사용할 경우 반환값으로 객체를 반환하여 모든 값이 있어야 하므로 *
을 사용하였다.