-
20221222 TIL JPA에서 @Query를 이용한 사용자 정의 쿼리 날리기!TIL 2022. 12. 22. 14:24
JpaRepository는 기본적인 메서드만을 제공해준다.
하지만 개발을 하다보면 JpaRepository가 기본적으로 제공하는 메서드보다 복잡한 쿼리를 날리고 싶은 경우가 생긴다.
예를 들면 특정 컬럼 값을 기준으로 조회하거나 정렬을 하는 등의 경우가 있을 수 있다.
이럴 때는 사용자 정의 쿼리를 사용해야 하는데,
사용자 정의 쿼리를 사용하는 방식에는 크게 쿼리 메서드 방식과 @Query 어노테이션을 사용하는 방법이 있다.
쿼리 메서드 방식은 메서드 이름을 정해진 컨벤션에 따라 작성하면
메서드 이름을 기반으로 적절한 쿼리가 수행되는 방식이다.
https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repository-query-keywords
위 링크에서 수많은 키워드들을 확인할 수 있다.
사용자 이름으로 사용자를 찾는 등의 간단한 쿼리는 위 방식을 이용해서 작업을 하면 매우 편리하다.
실제로 강의에서도 쿼리 메서드 방식을 이용해서 아샬님께서 알려주셨고,
메서드 이름만 잘 정의해주면 잘 동작하는 것을 보고 신기해하면서 편하게 사용하고 있었다.
그런데 포트폴리오를 진행하면서 도저히 쿼리메서드로 어떻게 메서드 이름을 정하면 좋을지 감이 잡히지 않는 경우가 발생했다.
나의 포트폴리오 웹페이지의 첫 화면에서는 특정 기간동안의 인기 질문이 표시된다.
인기 질문은 질문 좋아요 순으로 뽑힌다.
질문 엔티티는 좋아요를 한 사용자 id를 배열로 들고 있고,
그 질문들 중 좋아요를 한 사용자 id 배열의 크기가 큰 순으로 정렬을 하는 작업이 필요했다.
이 작업은 오히려 쿼리를 직접짜는 것이 더 효율적일 것 같았다.
그래서 내가 직접 쿼리를 짤 수 있는 방식을 찾아보았고,
@Query 어노테이션을 활용하면 된다는 것을 알게 되었다.
엔티티를 대상으로 쿼리를 작성하는 JPQL을 이용해서 쿼리를 작성해주고,
메서드 위에 어노테이션을 붙여주면 된다.
@Query("SELECT u FROM User u WHERE u.status = 1") Collection<User> findAllActiveUsers();
혹은 native SQL을 사용할 수도 있다.
@Query( value = "SELECT * FROM USERS u WHERE u.status = 1", nativeQuery = true) Collection<User> findAllActiveUsersNative();
나는 굳이 nativeSQL을 활용할 이유를 느끼지 못하여서 JPQL을 이용해서 작성해보았다.
쿼리가 잘 작동하는지 확인하기 위해 테스트를 작성해주자.
@SpringBootTest @Transactional @ActiveProfiles("test") class QuestionRepositoryTest { @Autowired QuestionRepository questionRepository; @Test void findAllByLikesCount() { List<LikeUserId> likeUserIds1 = List.of(new LikeUserId(1L), new LikeUserId(2L)); List<LikeUserId> likeUserIds2 = List.of(new LikeUserId(1L), new LikeUserId(2L), new LikeUserId(3L)); List<LikeUserId> likeUserIds3 = List.of(new LikeUserId(1L)); Question question1 = new Question(likeUserIds1); Question question2 = new Question(likeUserIds2); Question question3 = new Question(likeUserIds3); questionRepository.save(question1); questionRepository.save(question2); questionRepository.save(question3); List<Question> questions = questionRepository.findAllOrderByLikesCount(); assertThat(questions.get(0).getLikeUserIds()).hasSize(3); assertThat(questions.get(1).getLikeUserIds()).hasSize(2); assertThat(questions.get(2).getLikeUserIds()).hasSize(1); } }
public interface QuestionRepository extends JpaRepository<Question, Long> { @Query("SELECT q FROM Question q ORDER BY size(q.likeUserIds) DESC") List<Question> findAllOrderByLikesCount(); }
위 코드를 통해 좋아요 순으로 정렬된 질문 목록을 얻을 수 있게 되었다!
생각보다 직접 쿼리를 짜는 것도 많이 복잡하지 않았다.
간단한 쿼리일 때는 쿼리 메서드를 이용하고,
앞으로 데이터를 처리하면서 복잡한 경우에는 직접 쿼리를 짜는 방식을 이용하면 좋을 것 같다!
참고
'TIL' 카테고리의 다른 글
20221224 TIL enum으로 리팩토링! (0) 2022.12.24 20221223 TIL 게시글 인기순으로 정렬하기 (0) 2022.12.23 20221221 TIL 핸들링 해야하는 예외가 여러 개일 때 어떻게 하면 좋을까? (1) 2022.12.21 20221220 TIL @ElementCollection (0) 2022.12.20 20221219 TIL 값 객체를 활용하기 (0) 2022.12.19