Spring의 JPA 사용 중 데이터가 존재하는지 확인하는 방법으로 count 함수를 사용할 때가 있다.
단순히 하나의 데이터가 존재하는지 확인하는 경우(아이디 중복확인 등) count 함수를 사용하면 성능상에 문제가 발생할 수 있다.
성능에 실제로 문제가 발생하는지 알고싶어 검색 중 어떤 분이 작성하신 글에 해답이 나와 있었다.
Avoid Using COUNT() in SQL When You Could Use EXISTS()
요약하자면 SQL의 count함수를 사용할 때 보다 exists를 사용할 때가 성능이 1.3배 더 좋았다는 얘기다. 데이터가 훨씬 많은 상황에서는 더 많은 격차가 발생할 수 있을거로 예상됐다.
exists는 데이터가 존재하는지 확인만하면 되므로 데이터를 찾으면 쿼리를 종료하지만
count는 "총 몇개인지"를 확인한다 그러므로 모든 데이터를 스캔한다.
그 차이가 성능의 차이를 낸다.
그렇다면 JPQL에서 exists를 사용하면 된다는 것을 알았는데 어떻게 사용하는가?
JPQL은 select절에 exists를 지원하지 않는다.
Spring Data JPA에서는 메소드 쿼리라는게 존재하는데 메소드 이름을 적절히 설정을 하면 쿼리를 자동으로 만들어주는 기능이다.
메소드 쿼리에서는 exist를 사용 할 수 있다.
void existsById(String id);
public boolean existsById(String id) {
return from(Member)
.where(Member.id.eq(id))
.select(Member.id)
.fetchFirst() != null;
}
이렇게하면 내부적으로 하나의 데이터만 찾도록 쿼리에 limit 절이 들어간다.
jpql의 쿼리에 setMaxResult로 limit을 걸어주면 위 방법들과 같은 효과를 낸다.
public boolean existsById(String id) {
return em.createQuery("select m from Member m where m.id = :id")
.setParameter("id", id)
.setMaxResults(1)
.getSingleResult() != 0;
jpql에서 count를 사용할 시 찾은 데이터의 개수를 반환한다. 조건에 맞는 데이터가 전혀 존재하지 않는 경우에는 null을 반환하는 것이아닌, 0을 반환한다.
그러므로 getResultList()를 사용할 것이아닌 getSingleResult()를 사용하면 된다.