본문 바로가기
개발/스프링

[JPA] 조회 메소드에 파라미터 추가하기

by 카펀 2022. 7. 4.

쿼리를 직접 작성하여 저장한 후 불러와 사용하는 MyBatis와는 다르게, JPA는 미리 정해둔 요건에 따라 쿼리를 자동으로 생성한 후 실행해 줍니다.

그러다 보니 JPA를 처음 접하다 보면 단순한 CRUD 구현에도 헤매게 됩니다.

우연한 계기로 전달받은 파라미터를 조회 쿼리의 WHERE에 사용하는 방법을 찾아보게 되어 정리합니다.

 

1. JPA와 @Query의 역할

2. @Query를 통한 조회 시에 파라미터 전달하기

3. 테스트 코드로 검증하기

 

1. JPA와 @Query의 역할

먼저, 글을 작성하기에 앞서, 저는 JPA에 대해 매우 기초적인 지식만 가지고 있음을 알려 드립니다.

회사에서는 MyBatis를 이용하여 xml 파일에 쿼리를 직접 작성하는 개발을 하고 있으며, JPA는 여기서 진행한 개인 프로젝트에 한해서만 사용해 보았습니다.

 

통상적으로 JPA를 사용 시에는 쿼리를 직접 작성하지 않습니다. Java 코드를 이용하여 객체지향적으로 CRUD 기능을 간단히 구현할 수 있습니다.

하지만 때로는 직접 쿼리를 작성하여 실행하는 것이 더 효율적일 때도 있습니다. 그렇다고 MyBatis를 같이 끼워 쓰기엔 일관성도 떨어지고 보기에도 안 좋구요.

 

이럴 때 @Query를 통해 쿼리를 직접 작성할 수 있습니다. 이때 작성하는 SQL은 JPQL이라고 합니다.

관련해서 제가 예전에 적었던 글이 있습니다.

@Query를 이용하면 직접 쿼리를 작성하여, 원하는 로직을 섬세하게 구현할 수 있습니다.

 

2. @Query를 통한 조회 시에 파라미터 전달하기

기존에 아래와 같은 조회 메소드가 있습니다.

public interface PostsRepository extends JpaRepository<Posts, Long> {

    @Query("SELECT p FROM Posts p ORDER BY p.id DESC")
    List<Posts> findAllDesc();
}

findAllDesc는 이름 그대로, Posts 테이블 내의 모든 데이터를 조회 후, ID를 내림차순 (desc)으로 정렬하여 리턴합니다.

이 때 조회하는 Posts 테이블은 아래와 같이 구성되어 있습니다.

 

하지만 만약에 이 중에서 특정 데이터만 조회하고 싶다면?

예를 들어서 [id = 5, author = '정규호'] 인 데이터를 조회하고 싶다면?

 

쿼리를 직접 작성한다면 아래와 같이 되겠죠.

SELECT * FROM Posts P
WHERE
	P.id = 1
    and P.author = '정규호'

 

@Query를 이용할 때는 아래와 같이 작성할 수 있습니다.

 

public interface PostsRepository extends JpaRepository<Posts, Long> {

    @Query("SELECT p FROM Posts p WHERE p.id = :postId AND p.author = :creatorName ORDER BY p.id DESC")
    List<Posts> findAllDesc(@Param("postId") Long postId, @Param("creatorName") String creatorName);
}

 

findAllDesc가 호출될 때 파라미터 postId, creatorName을 전달받고, 이를 쿼리의 WHERE 부분에 인자로 전달합니다.

변수 앞에 콜론 (:)을 붙여 주고, 메소드의 파라미터 내에 구성 요소를 정의해 주면 됩니다.

 

3. 테스트 코드로 검증하기

위에서 작성한 findAllDesc가 잘 작동하는지 테스트 코드를 통해 검증해 보려고 합니다.

아래와 같이 테스트 코드 메소드를 작성하였습니다.

 

@Test
@WithMockUser(roles = "USER")
public void Posts_파라미터_조회된다() throws Exception {
    // given
    Posts savedPosts = postsRepository.save(Posts.builder()
            .title("title")
            .content("content")
            .author("author")
            .build());

    Posts savedPosts2 = postsRepository.save(Posts.builder()
        .title("제목")
            .content("콘텐츠")
            .author("정규호")
            .build());

    Long updateId = savedPosts.getId();
    Long updateId2 = savedPosts2.getId();

    String expectedAuthor = "정규호";

    // when
    List<Posts> map = postsRepository.findAllDesc(updateId2, expectedAuthor);

    // then
    assertThat(map.get(0).getId()).isEqualTo(updateId2);
    assertThat(map.get(0).getAuthor()).isEqualTo(expectedAuthor);
}

코드 구성이 크게 복잡하지 않죠?

given: 글 2개를 저장합니다. 내부 데이터는 모두 다릅니다.

when: [id = 2, author = '정규호'] 를 findAllDesc에 인자로 넘겨줍니다.

then: 조회한 글의 [id = 2, author = '정규호'] 라면 테스트가 성공합니다.

 

이후 테스트를 실행해 보았습니다.

테스트가 성공한 것을 확인할 수 있습니다.

댓글