ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 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 배열의 크기가 큰 순으로 정렬을 하는 작업이 필요했다.

    https://cdn.educba.com/academy/wp-content/uploads/2020/02/JPQL.jpg

    이 작업은 오히려 쿼리를 직접짜는 것이 더 효율적일 것 같았다.

    그래서 내가 직접 쿼리를 짤 수 있는 방식을 찾아보았고,

    @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();
    }

    위 코드를 통해 좋아요 순으로 정렬된 질문 목록을 얻을 수 있게 되었다!

     

    생각보다 직접 쿼리를 짜는 것도 많이 복잡하지 않았다.

    간단한 쿼리일 때는 쿼리 메서드를 이용하고,

    앞으로 데이터를 처리하면서 복잡한 경우에는 직접 쿼리를 짜는 방식을 이용하면 좋을 것 같다!

     

    참고

    https://www.baeldung.com/spring-data-jpa-query

    댓글

Designed by Tistory.