nu_s

[Spring Data JPA] 쿼리 메서드 🍀 본문

Spring Data JPA

[Spring Data JPA] 쿼리 메서드 🍀

woochii 2023. 11. 29. 18:39
728x90
반응형

쿼리메서드

  • 이전에 쿼리 메서드에 대해 간단하게 언급했었는데, 조금 더 깊게 들어가보도록 하자
  • 만약 도메인에 특화되어 있거나, 검색 조건이 들어간 쿼리는 어떻게 해결해야 할까?
  • 이 방법을 해결하는 3가지 방법이 있다.
    1. 메서드 이름으로 쿼리 생성하기
    2. 메서드 이름으로 JPA NamedQuery 호출하기
    3. @Query 어노테이션을 사용해서 리포지토리 인터페이스에 쿼리 직접 정의하기

 

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

  • 메서드 이름을 분석해서 JPQL 쿼리를 실행한다.
  • 이름과 나이를 기준으로 회원을 조회하려면 어떻게 해야할까?

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

 

테스트

@Test
public void findByUsernameAndAgeGreaterThan() {
    Member m1 = new Member("AAA", 10);
    Member m2 = new Member("AAA", 20);
    memberJpaRepository.save(m1);
    memberJpaRepository.save(m2);

    List<Member> result = memberJpaRepository.findByUsernameAndAgeGreaterThan("AAA", 15);

    assertThat(result.get(0).getUsername()).isEqualTo("AAA");
    assertThat(result.get(0).getAge()).isEqualTo(20);
    assertThat(result.size()).isEqualTo(1);
}
  • 위의 코드처럼 쿼리를 만들고 테스트를 돌려보면 간단하게 통과한다.
  • 그러나 우리는 스프링 데이터 JPA를 통해 코드를 더 간단히 만드는 방법을 배웠다.

스프링 데이터 JPA

public interface MemberRepository extends JpaRepository<Member, Long> {
    List<Member> findByUsernameAndAgeGreaterThan(String username, int age);
}
  • 이렇게 구현부 없이 인터페이스로 생성해준 뒤 같은 테스트 코드를 실행해도 통과한다.
  • 단순한 쿼리도 아닌데 왜 구현부가 없이도 잘 동작하는걸까?
  • 이유는 바로 메서드 이름에 있다.

https://docs.spring.io/spring-data/jpa/reference/jpa/query-methods.html

 

JPA Query Methods :: Spring Data JPA

As of Spring Data JPA release 1.4, we support the usage of restricted SpEL template expressions in manually defined queries that are defined with @Query. Upon the query being run, these expressions are evaluated against a predefined set of variables. Sprin

docs.spring.io

  • 링크를 따라 들어가보면 메서드의 이름과 쿼리의 관계가 나와있다.
  • 이를 참고하여 코드를 만들면 된다.

 

스프링 데이터 JPA가 제공하는 쿼리 메서드 기능

  • 조회
    • find...By
    • read...By
    • query...By
    • get...By
  • COUNT : count...By
  • EXISTS : exists...By
  • 삭제
    • delete...By
    • remove...By
  • DISTINCT
    • findDistinct
    • findMemberDistinctBy
  • LIMIT
    • findFirst3
    • findFirst
    • findTop
    • findTop3

 

장점

  • 별도로 쿼리를 작성해주지 않아도 이름만 잘 작성하면 편리하게 사용할 수 있다.

단점

  • 파라미터가 많아질수록 이름이 점점 길어진다는 단점이 있다.
    • 쿼리가 간단할 때만 사용하자

2. JPA NamedQuery

@Entity
@NamedQuery(
        name = "Member.findByUsername",
        query = "select m from Member m where m.username = :username"
)
public class Member {
    ...
}
  • 두번째 방법은 NamedQuery이다.
  • 엔티티에 @NamedQuery 어노테이션을 사용해서 사용할 쿼리와 그 쿼리의 이름을 지정해준다.
public interface MemberRepository extends JpaRepository<Member, Long> {

    @Query(name = "Member.findByUsername") //생략 가능
    List<Member> findByUsername(@Param("username") String username);
    
}
  • 리포지토리에서 @Query 어노테이션을 통해 불러올 쿼리의 이름을 적어준다.
  • @Param으로 파라미터 이름을 설정해준다.
  • 여기서 @Query는 생략이 가능하다.
  • 만약 생략했다면 우선순위에 따라 처리한다.
    1. 먼저 메서드 이름과 같은 NamedQuery를  Member에서 탐색한다.
    2. 없다면 처음에 설명한 방식인 메서드 이름으로 쿼리를 생성한다.

 

장점

  • 오타가 났어도 애플리케이션 실행 시점에 문법 오류를 발견해준다.

 

그러나 실무에서 잘 사용하지 않으니, 이런 방법이 있다는 것만 알아두자.


3. @Query

3-1. 리포지토리 메서드에 쿼리 정의하기

  • @Query 어노테이션을 사용하여 쿼리를 직접 작성하는 방법이다.
  • 실행할 메서드에 정적 쿼리를 직접 작성하므로 이름 없는 NamedQuery라고 할 수 있다.
public interface MemberRepository extends JpaRepository<Member, Long> {

    @Query("select m from Member m where m.username = :username and m.age = :age")
    List<Member> findUser(@Param("username") String username, @Param("age") int age);
    
}

 

장점

  • JPA NamedQuery처럼 애플리케이션 실행 시점에 문법 오류를 발견해준다.
    • 파라미터가 많고 쿼리가 복잡한 경우 이 방식을 사용하자.

 

3-2. 값 조회하기

public interface MemberRepository extends JpaRepository<Member, Long> {

    @Query("select m.username from Member m")
    List<String> findUsernameList();

}
  • 값 타입은 이런 방식으로 조회할 수 있다.
  • @Embedded 타입도 가능하다.

3-3. DTO 조회하기

@Data
@AllArgsConstructor
public class MemberDto {
    private Long id;
    private String username;
    private String teamName;
}
public interface MemberRepository extends JpaRepository<Member, Long> {

    @Query("select new study.datajpa.dto.MemberDto(m.id, m.username, t.name) from Member m join m.team t")
    List<MemberDto> findMemberDto();
}
  • DTO로 조회할 땐 이렇게 조회하면 된다.
  • 단, new 명령어를 사용해야 한다.

출처 : 인프런 실전! 스프링 데이터 JPA

728x90
반응형