위찬의 기술 블로그

JPQL과 Native query

JPA와 함께 많이 사용하는 ORM 라이브러리

JPQL 개요

JPA를 사용하다보니 JPA만으로 간단하게 해결되지 않는 조회가 필요했다.
JPQL은 SQL을 추상화해 SQL처럼 보이는 Query Language이다.

JPQL 사용하기

Repository 클래스에서 @Query 어노테이션으로 사용한다.

// BoardRepository.java ...
@Query(value="SELECT b FROM Board b")
List<Board> findAllBy();

문제 상황 1

JPQL 사용 중 FROM절 서브쿼리를 사용하려고 했는데, JPQL은 FROM 서브쿼리를 지원하지 않는다고 한다.
추천하지는 않지만, nativeQuery 옵션으로 추상화되지 않은 SQL을 직접 사용할 수 있다.

해결: Native Query 사용해보기

// BoardRepository.java ...
@Query(value="SELECT board_id, created_at, nickname, title, views, writer "
    + "FROM ( "
    + "  SELECT * "
    + "  FROM board "
    + "  WHERE TIMESTAMPDIFF(HOUR, created_at, NOW()) < 25 "
    + ") board "
    + "ORDER BY views DESC "
    + "LIMIT 5;", nativeQuery=true)
List<BoardListMapping> findDailyTop();

이걸로 해결인가 싶었다.

문제 상황 2

findDailyTop()으로 데이터를 조회했더니, board_id, create_at 컬럼의 데이터가 null으로 조회되었다.

찾아보니 JPQL은 nativeQuery의 snake_case를 camelCase로 변환해주지 않았다.
따라서 boardId, createdAt 등으로 정의된 엔티티 컬럼이 조회되지 않은 것이었다.

그렇다면..

해결: alias 사용하기

@Query(value="SELECT board_id AS 'boardId', created_at AS 'createdAt', nickname, title, views, writer "
    + "FROM ( "
    + "  SELECT * "
    + "  FROM board "
    + "  WHERE TIMESTAMPDIFF(HOUR, created_at, NOW()) < 25 "
    + ") board "
    + "ORDER BY views DESC "
    + "LIMIT 5;", nativeQuery=true)
List<BoardListMapping> findDailyTop();

조회 쿼리에 Alias를 주어, board_id를 boardId로 변환해 해결했다.

사족

JPA, JPQL은 추상화되어 어떤 Database에도 이식 가능함이 장점인데, nativeQuery가 포함되는 순간 유연성이 떨어진다.
Native Query를 사용하지 않는 다른 방법을 생각해보자.