[Spring Data JPA] 쿼리 메서드와 @Query를 이용한 사용자 정의 쿼리

hyewon jeong·2023년 1월 19일
0

Spring

목록 보기
22/59

본 글을 @Query 에 대해 공부를 위해 참고에 적힌 출처에 의해 작성했습니다.

JpaRepository

  • 기본적으로 CRUD 메서드 제공
    만약 JpaRepository 가 제공하지 않는 기능들을 사용해야할 때는 어떻게 할까?

예를 들어 사용자 PK가 아닌 이름으로 조회하고 싶을 때, 사용자 이름과 이메일로 조회하고 싶을때, 나이가 20살 이상인 사용자를 조회하고 싶을 때 등등.

그럼 어쩔 수 없이 사용자 정의 쿼리를 사용해야 한다.


사용자 정의 쿼리

말 그대로 JPA가 자동으로 생성하는 쿼리를 사용하는게 아닌 사용자가 정의한 대로 쿼리가 생성 혹은 데이터베이스에 종속적인 Native Query 가 생성 되는 것을 말 한다.

JPA 에서 사용자 정의 쿼리를 사용하는 방법에는 여러 방법이 존재한다.

  1. Named Query
  2. 쿼리 메서드
  3. @Query 어노테이션

1. Named Query

네임드 쿼리는 말 그대로 쿼리에 이름을 부여하는 방법인데, 컴파일시 타입체크, 가독성과 같은 부분에서 문제가 조금 있기 때문에 @Query 어노테이션을 사용할 우리에게는 당장 필요하지는 않다.

그러므로 우리는 2번 3번, 쿼리 메서드와 @Query 에 더 집중하면 된다.


## 2. 쿼리 메서드 Intellij 에서 Repository 인터페이스 에 메서드를 정의해보자.

Optional find 까지만 쳐도 아래와 같은 snippet 이 등장한다.

메서드 이름으로 우리가 원하는 기능을 수행할 쿼리가 자동으로 생성되게 할 수 있다.

👍 쿼리 메서드 기능은 Spring Data JPA 에서 정해놓은 네이밍 컨벤션을 지키면 JPA가 해당 메서드 이름을 분석해서 적절한 JPQL 을 구성한다.

🔑Spring Data JPA-Query Method 공식 문서

하지만 위의 쿼리메서드 기능이 워낙 강력하다고 해도 모든 사용자의 니즈를 파악하긴 힘들다.

어떤 프로젝트에서는 Native Query를 사용해야하는 곳도 필요할 것이고, 다양한 조합으로 쿼리를 짜야하는 상황이 올 때는 어떻게 할까?

3. @Query ( JPQL )

그런 상황, 개발자가 원하는 쿼리를 직접 짜야 하는 그런 상황이 올 때 @Query 는 아주 강력하다.

사용법

  • @Query는 실행할 메서드 위에 정적 쿼리를 작성 한다.
    여기 들어가는 쿼리는 JPQL 이라는 쿼리가 들어가야 한다.

JQPL

Java Persistence Query Language 인 JPQL은 객체지향 쿼리로 JPA가 지원하는 다양한 쿼리 방법 중 하나이다.

기존에 SQL 중심적 개발에 익숙한 우리에게는 어떻게 보면 가장 단순한 방법이기도 하다.

SQL과 JPQL의 차이점이 있다고 한다면

SQL
테이블을 대상으로 쿼리
JPQL
엔티티 객체를 대상으로 쿼리
를 하게 된다.

쿼리문 내부에 다음과 같이 참조변수.필드 와 같은 형태로 사용된다.

select
    m.username,
    m.address
from
    Member m
where
    m.age>18

### 다시 @Query 로 돌아와서! **@Query는 JpaRepository 를 상속하는 인터페이스에서 사용한다.**
public interface UserRepository extends JpaRepository<User, Long> {
    @Query("쿼리문")
    List<User> methodName();

이와 같은 형태로 주로 사용된다.

만약 age가 20살 이상인 사람을 조회한다고 해보면 JPQL은 다음과 같이 사용될 것이다.

Sring jpql = "select u from User u where u.age > 20";

중요한 것은 우리는 테이블을 대상으로 쿼리를 날리는게 아니라 엔티티를 대상으로 날린다는 것이다.

파라미터 바인딩 시키기

우리가 이걸 하는 이유는 뭘까?

바로 사용자 정의 쿼리를 하는 것이다.

즉, 아래의 methodName() 에 들어갈 파라미터를 우리의 쿼리문에 넣는 것이다.

public interface UserRepository extends JpaRepository<User, Long> {
    @Query("쿼리문")
    List<User> methodName();

이를 파리미터 바인딩이라고 한다.

파라미터 바인딩

  • 쿼리에 작성되는 특정 속성을▶ 매개변수로 매핑하는 것을 말한다.

  • 쿼리에 매개변수를 매핑하는 방식에는 이름을 기준으로 하는 방식과 위치를 기준으로 하는 방식이 있다.

a) 이름 기반 바인딩

Query query = em.createQuery("select m from Member m where m.username =: username")
                .setParameter("username", usernameParam);
  • 이름 기준 바인딩은 =: 연산자를 사용한다.

  • 위의 예시 코드에서 확인할 수 있듯이, 메서드 체이닝을 사용할 수 있다.


b) 위치 기반 바인딩

Query query = em.createQuery("select m from Member m where m.username =? 1")
                .setParameter(1, usernameParam);
  • 위치 기준 바인딩은 =? 연산자를 사용한다.

  • 위의 예시 코드에서 확인할 수 있듯이, 메서드 체이닝을 사용할 수 있다.


    c) 권장 방법

  • 매개변수를 바인딩할 때에는 이름 기반 바인딩을 사용하는 것을 권장한다.

  • 위치 기반 바인딩을 사용하면 중간에 새로운 매개변수를 추가하는 경우, 순서가 밀리기 때문이다.

  • 또한 숫자를 통해서 어떤 위치의 매개변수가 무엇을 의미하는지 쉽게 파악할 수 없다.

  • 즉, 가독성이 떨어져 유지보수 상황에서 비효율적이다.


👍 이름 기반으로 파라미터 바인딩을 하고 파라미터에 @Param("") 어노테이션으로 메서드에 들어오는 파라미터가 어떤 이름으로 지정될 지 정할 수 있다.


public interface UserRepository extends JpaRepository<User, Long> {
    @Query("select u from User u where u.username = :name")
    List<User> methodName(@Param("name") String username);

참고
https://wonit.tistory.com/470
https://devraphy.tistory.com/566?category=1059635

profile
개발자꿈나무

0개의 댓글