QueryDsl에서 결과를 가져오는 메서드, fetchFirst()와 fetchOne()의 차이
QueryDsl

QueryDsl에서 결과를 가져오는 메서드, fetchFirst()와 fetchOne()의 차이

1. 알고자 하는 것

회사에서 mybatis 쿼리를 querydsl로 전환하는 작업 중, update_date 기준으로 정렬된 n개의 결과 중 첫번째 결과만을 가져오는 쿼리가 있었다.

그런데, 쿼리를 실행하니 다음과 같은 Exception이 발생했다.

Caused by: javax.persistence.NonUniqueResultException: result returns more than one elements
at org.hibernate.jpa.internal.QueryImpl.getSingleResult....

단건의 결과가 아닌 여러 건의 결과(Non Unique Result)가 반환되어 발생한 오류였다.

원인이 바로 짐작이 가 쿼리를 확인해보니, 역시나 QueryDsl을 통해 쿼리를 짤 때 fetchFirst()가 아닌 fetchOne()을 사용했었던 것이다.

 

First와 One, 네이밍이 워낙 비슷한 까닭에 영포자인 나는 이전에 얼추 둘의 차이를 알고 있었음에도 불구하고 실수하고 말았다.

다시는 헷갈리지 않도록 사소한 부분이지만 확실하게 익혀두고자 fetchFirst와 fetchOne의 차이를 정리하고 가본다.

영어는 포기했어도 개발은 포기하지 말자.

이제는 더 이상 물러날 곳이 없따

 

  • fetchFirst()와 fetchOne()의 차이

2. 알게된 것

fetchFirst()와 fetchOne()의 차이

  • 사실 fetchFirst와 fetchOne의 차이는 QueryDsl 공식문서만 봐도 쉽게 찾아볼 수 있다.

출처 : QueryDsl 공식문서 - Fetchable

  • fetchFirst() : 첫 번째 결과를 가져오거나 결과가 없으면 null을 가져옵니다.
  • fetchOne() : 고유한 결과를 가져오거나 결과가 없으면 null을 가져옵니다.
  • 공식문서에 정의된 내용에 따르면 fetchFirst()는 첫 번째 결과, fetchOne은 고유한 결과를 가져온다는 차이가 있다.
  • 추상적으로는 이것만으로 구분지을 수 있지만, fetchFirst()와 fetchOne()의 내부 코드를 확인해보면 더 중요한 차이가 있다.
@Override
public T fetchOne() throws NonUniqueResultException {
    try {
        Query query = createQuery(getMetadata().getModifiers(), false);
        return (T) getSingleResult(query);
    } catch (javax.persistence.NoResultException e) {
        logger.log(Level.FINEST, e.getMessage(), e);
        return null;
    } catch (javax.persistence.NonUniqueResultException e) {
        throw new NonUniqueResultException(e);
    } finally {
        reset();
    }
}
  • fetchOne()은 getSingleResult(query)를 반환한다.
  • getSingleResult()는 결과가 여러 건일 때 NonUniqueResultException을 throw한다.
  • 이로 인해, fetchOne()을 사용하게 되면 결과가 여러 건이면 NonUniqueResultException이 발생한다.
@Override
public final T fetchFirst() {
    return limit(1).fetchOne();
}
  • fetchFirst()는 limit(1).fetchOne()을 반환한다.
  • 즉, limit(1)을 통해 미리 결과를 단건으로 치환하고 fetchOne()을 수행한다.
  • 이로 인해, fetchFirst()을 사용하게 되면 결과가 여러 건이어도 내부적으로 미리 limit(1)을 수행하여 가장 위의 한 건만 조회된다.

 

 

 

3. 정리

  • QueryDsl을 사용하고 결과를 한 건만 조회해야 할 때, 결과가 명확하게 한 건만 조회됨이 보장되지 않는다면 fetchOne()은 NonUniqueResultException을 던질 가능성이 있다.
  • 결과를 한 건만 조회해야 할 때, 결과가 명확하게 한 건만 조회됨이 보장되지 않는다면 내부적으로 limit(1)을 수행하는 fetchFirst()를 사용하자. 
  • 결과가 없을 때 null을 반환한다는 점은 fetchFirst()와 fetchOne() 모두 동일하다.

Reference

 

QueryDsl 공식문서 - Fetchable : 

http://querydsl.com/static/querydsl/4.4.0/apidocs/com/querydsl/core/Fetchable.html

 

Fetchable (Querydsl 4.4.0 API)

fetchFirst T fetchFirst() Get the first result of Get the projection or null if no result is found Returns: first result or null

querydsl.com