간단한 기능만을 구현하여 JPA 에서 제공하는 메서드로만 해결을 하다가, 조금 복잡한 쿼리를 실행해야 하는 경우가 생겼다.

이전에는 조금 복잡한 쿼리가 필요하더라도 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 를 사용할 경우 반환값으로 객체를 반환하여 모든 값이 있어야 하므로 * 을 사용하였다.