스프링 데이터 JPA 쿼리 메소드 기능

구본식·2023년 2월 4일
1

Spring Data JPA

목록 보기
3/8
post-thumbnail
post-custom-banner

1. 메소드 이름으로 쿼리 생성

Spring Data JPA메소드 이름을 분석하여 JPQL을 생성하고 실행한다.
동작 방식을 살펴보기 위해 순수 JPA코드와 Spring Data JPA 코드를 비교해보겠다.

순수 JPA 코드

	//순수 JPA- 메소드 이름으로 쿼리 생성
    public List<Member> findByUsernameAndAgeGreaterThan(String username, int age){
        return em.createQuery("select m from Member m where m.username=:username and m.age>:age")
                .setParameter("username", username)
                .setParameter("age",age)
                .getResultList();
    }

순수 JPA를 사용하여 username, age를 조건으로 구성한 JPQL이다.

Spring Data JPA

	//Spring Data JPA- 메소드 이름으로 쿼리 생성
    List<Member> findByUsernameAndAgeGreaterThan(String username, int age);

    //Spring Data JPA- 메소드 이름으로 쿼리 생성
    List<Member> findTop3TestBy();

같은 동작을 하는 코드를 Spring Data JPA로 작성한 코드이다.

findByUsernameAndAgeGreaterThan이름의 메소드를 선언하게 되면 Spring Data JPA는
...where m.username=:username and m.age>:age의 JPQL를 생성하게 된다.!

공식문서에 나와있는 쿼리 메소드 기능중 조회를 간단히 살펴보자면,
find...By = read...By 등으로 메소드를 설계가 가능하다.

...의 부분에는 메소드를 식별하기 위한 설명을 자유롭게 추가 가능하고 By 뒷부분부터는 조건문이 위치하게 된다.
만약 By절 뒤에 아무것도 적지 않는다면 아무런 조건을 사용하지 않는것이다.

이밖에도,

  • COUNT : count...By
  • EXISTS : exists...By
  • 삭제: delete...By
  • LIMIT : findFirst3, findFirst, findTop3등

많은 쿼리 메소드 기능을 제공한다. 아래 Spring 공식문서를 통해 자세히 볼수 있다.
https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repositories.query-methods.query-creation

이처럼 Spring Data JPA는 메소드 이름을 통해 정적 쿼리를 미리 정의할 수 있기 때문에 컴파일 시점에서 문법적인 오류를 조기에 발견할 수 있는 큰 장점이 있다!!!

하지만 조건으로 사용하는 필드가 많아지면, 메소드의 이름이 장황해지는 단점이 발생한다.

이를 해결할 수 있는 방식을 뒤에 소개해보겠다.

참고💡
조건문에 넣을 필드가 2이하까지는 "메소드 이름으로 쿼리 생성 방식"을 사용하고 초과할때부터는 뒤에 나오는 "@Query, 레포지토리 메소드 쿼리정의" 방식을 사용하는것이 좋다고 한다.


2. JPA NamedQuery

이 방식은 @NamedQuery 어노테이션을 사용하여 쿼리를 미리 정의해논후 이름을 할당해 해당 이름을 호출하여 사용하는 방식이다.

마찬가지로 순수 JPA 코드와 Spring Data JPA 코드를 비교하며 공부해보겠다.

Member.Entity

...
@NamedQuery( //Named 쿼리
        name = "Member.findByUsername",
        query = "select m from Member m where m.username = :username"
)
public class Member {
	...
}

Entity에 @NamedQuery를 사용하여 쿼리의 이름과 JPQL을 미리 선언하게 된다.

순수 JPA 코드

	 //순수 JPA - Named 쿼리 호출
    public List<Member> findByUsername(String username){
        return em.createNamedQuery("Member.findByUsername")
                .setParameter("username", username)
                .getResultList();
    }

createNamedQuery를 사용하여 미리 정의해논 쿼리이름을 호출한 후,
필요한 파라미터를 세팅해주는 방식으로 사용할 수 있다.

Spring Data JPA

	//Spring Data JPA - Named 쿼리 호출
    @Query(name = "Member.findByUsername") //생략 가능
    List<Member> findByUsername(@Param("username")String username);

@Query 어노테이션을 사용하여 쿼리문 이름을 호출할수 있게 된다.

또한 필요한 파라미터들은 @Param 어노테이션을 통해 매핑 시켜준다.

참고로
@Param("username")) -> "username" 이름과
select ... =:username" -> ":"username" 이름이 같아야지 정상적으로 파라미터 매핑이 된다.!

또한 위의 메소드에서는 @Query 어노테이션이 없어도 정상동작하게 된다.

왜냐하면,
Spring Data JPA는 정의한 메소드의 JPQL문을 생성할때 아래와 같은 순서가 있기 때문이다.

  1. "엔티티.메소드 이름"과 같은 이름의 @NamedQuery를 먼저 찾는다.
    • 엔티티 : Member, 메소드 이름 : findByUsername => Member.findByUsername 이름의 @NamedQuery를 찾음
  2. 만약 없을시 메소드 이름으로 쿼리 생성을 통해 JPQL을 생성한다.

이 방식은 메소드 이름으로 쿼리 생성과 마찬가지로 @NameQuery 를 통해 정적쿼리를 미리 정의하기 때문에 컴파일 시점에서 문법 오류를 발견할수 있는 장점이 있다.!!

하지만 "엔티티에 JPQL과 쿼리 이름을 미리 정의하고 정의한 이름을 호출"와 같이 번거로움이 많이 발생한다.

이러한 @NameQuery의 장점과 단점을 보안한 방식이 다음 나올 방식이다.


3. @Query, 레포지토리 메소드에 쿼리 정의

이 방식은 @NamedQuery 방식처럼 엔티티에 미리 쿼리문을 생성하는것이 아니라,
레포지토리 메소드에 @Query 사용하여 바로 JPQL문을 작성할수 있는 방식이다.

 	//Spring Data JPA - 리포지토리 메소드에 쿼리 정의하기
    @Query("select m from Member m where m.username= :username and m.age= :age")
    List<Member> findMember(@Param("username") String username, @Param("age") int age);

보다시피 @Query 어노테이션에 JPQL문을 바로 작성할 수 있다.

실행할 메서드에 정적쿼리를 바로 작성하므로 이름 없는 Named 쿼리라고 할 수 있다.

실무에서 가장 많이 사용하는 방식이라고 한다.
정적 쿼리에 대해서 작성할때 파라미터가 소수일때는 "메소드 이름으로 쿼리생성" 방식을 사용하고 파라미터가 증가할때는 "@Query,레포지토리 메소드에 쿼리 정의"을 사용하면 될거 같다!


4. 그 밖에

그 밖에도 DTO로 직접 조회하기, 파라미터 바인딩, 다양한 반환타입 등도 있다.

DTO 직접 조회 같은 경우에는 QueryDSL방식이 더 편한거 같다.
(쿼리 결과를 DTO로 생성할때 생성자를 쓰는 방식이 QueryDSL이 더 편하기 때문에)

  • 다양한 반환타입
    • 컬렉션(List) : 결과가 없을시 null 아닌 Empty로 채워준다.
    • 단건 조회(엔티티, Optional)
      • 결과가 없을시 null로 반환 : 단건 조회에서 결과가 없을시 예외가 발생하는데
        Spring Data JPA가 내부적으로 try~ catch로 처리하여 null을 대신 반환해준다.
      • 결과가 2건 이상 : 단건 조회에서 결과가 2개 이상이면 예외 발생
profile
백엔드 개발자를 꿈꾸며 기록중💻
post-custom-banner

0개의 댓글