본 글을 @Query 에 대해 공부를 위해 참고에 적힌 출처에 의해 작성했습니다.
예를 들어 사용자 PK가 아닌 이름으로 조회하고 싶을 때, 사용자 이름과 이메일로 조회하고 싶을때, 나이가 20살 이상인 사용자를 조회하고 싶을 때 등등.
그럼 어쩔 수 없이 사용자 정의 쿼리를 사용해야 한다.
말 그대로 JPA가 자동으로 생성하는 쿼리를 사용하는게 아닌 사용자가 정의한 대로 쿼리가 생성 혹은 데이터베이스에 종속적인 Native Query 가 생성 되는 것을 말 한다.
JPA 에서 사용자 정의 쿼리를 사용하는 방법에는 여러 방법이 존재한다.
네임드 쿼리는 말 그대로 쿼리에 이름을 부여하는 방법인데, 컴파일시 타입체크, 가독성과 같은 부분에서 문제가 조금 있기 때문에 @Query 어노테이션을 사용할 우리에게는 당장 필요하지는 않다.
그러므로 우리는 2번 3번, 쿼리 메서드와 @Query 에 더 집중하면 된다.
Optional find 까지만 쳐도 아래와 같은 snippet 이 등장한다.
메서드 이름으로 우리가 원하는 기능을 수행할 쿼리가 자동으로 생성되게 할 수 있다.
👍 쿼리 메서드 기능은 Spring Data JPA 에서 정해놓은 네이밍 컨벤션을 지키면 JPA가 해당 메서드 이름을 분석해서 적절한 JPQL 을 구성한다.
🔑Spring Data JPA-Query Method 공식 문서
하지만 위의 쿼리메서드 기능이 워낙 강력하다고 해도 모든 사용자의 니즈를 파악하긴 힘들다.
어떤 프로젝트에서는 Native Query를 사용해야하는 곳도 필요할 것이고, 다양한 조합으로 쿼리를 짜야하는 상황이 올 때는 어떻게 할까?
그런 상황, 개발자가 원하는 쿼리를 직접 짜야 하는 그런 상황이 올 때 @Query 는 아주 강력하다.
Java Persistence Query Language 인 JPQL은 객체지향 쿼리로 JPA가 지원하는 다양한 쿼리 방법 중 하나이다.
기존에 SQL 중심적 개발에 익숙한 우리에게는 어떻게 보면 가장 단순한 방법이기도 하다.
SQL과 JPQL의 차이점이 있다고 한다면
SQL
테이블을 대상으로 쿼리
JPQL
엔티티 객체를 대상으로 쿼리
를 하게 된다.
쿼리문 내부에 다음과 같이 참조변수.필드 와 같은 형태로 사용된다.
select
m.username,
m.address
from
Member m
where
m.age>18
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