JPQL과 native query

업데이트:

JPQL 개요

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

JPQL 사용하기

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

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

문제 상황 1

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

해결: Native Query 사용해보기

1
2
3
4
5
6
7
8
9
10
// 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 사용하기

1
2
3
4
5
6
7
8
9
@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를 사용하지 않는 다른 방법을 생각해보자.

댓글남기기