본문 바로가기

JPA

[Spring Data JPA] 쿼리 메서드 기능

쿼리 메소드 기능

한마디로 말하자면 스프링 데이터 JPA가 제공하는 마법 같은 기능이다.

 

쿼리 메소드 기능 3가지

  1. 메소드 이름으로 쿼리 생성
  2. 메소드 이름으로 JPA NamedQuery 호출
  3. @Query 어노테이션을 사용해서 리파지토리 인터페이스에 쿼리 직접 정의

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

public interface MemberRepository extends JpaRepository<Member, Long> {
    List<Member> findByUsernameAndAgeGreaterThan(String username, int age);
    
}
  • 이름과 나이를 기준으로 회원을 조회하려면?
  • 스프링 데이터 JPA는 메소드 이름을 분석해서 JPA criteria API를 사용하여 쿼리를 생성하지만 JPQL을 변환하고 실행

 

쿼리 메소드 필터 조건

 

JPA Query Methods :: Spring Data JPA

By default, Spring Data JPA uses position-based parameter binding, as described in all the preceding examples. This makes query methods a little error-prone when refactoring regarding the parameter position. To solve this issue, you can use @Param annotati

docs.spring.io

 

 

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

  • 조회: find…By ,read…By ,query…By get…By
  • COUNT: count…By ➔ 반환타입 long
  • EXISTS: exists…By ➔ 반환타입 boolean
  • 삭제: delete…By, remove…By ➔ 반환타입 long
  • DISTINCT: findDistinct, findMemberDistinctBy
  • LIMIT: findFirst3, findFirst, findTop, findTop3

이 기능은 엔티티의 필드명이 변경되면 인터페이스에 정의한 메서드 이름도 꼭 함께 변경해야 한다.

그렇지 않으면 애플리케이션을 시작하는 시점에 오류가 발생한다. 
이렇게 애플리케이션 로딩 시점에 오류를 인지할 수 있는 것이 스프링 데이터 JPA의 매우 큰 장점이다.

하지만 조건을 추가할수록 메서드 이름이 길어지는 부분은 단점인 것 같다.


2. JPA NamedQuery

JPA의 NamedQuery를 호출할 수 있음

 

JPA를 직접 사용해서 Named 쿼리 호출

 

스프링 데이터 JPA로 NamedQuery 사용

  • @Query 를 생략하고 메서드 이름만으로 Named 쿼리를 호출할 수 있다.

스프링 데이터 JPA로 Named 쿼리 호출

  • 스프링 데이터 JPA는 선언한 "도메인 클래스 + .(점) + 메서드 이름"으로 Named 쿼리를 찾아서 실행
  • 만약 실행할 Named 쿼리가 없으면 메서드 이름으로 쿼리 생성 전략을 사용한다. 
  • 필요하면 전략을 변경할 수 있지만 권장하지 않는다.
  • 실무에서 Named Query를 직접 등록해서 사용하는 일은 드물다. 
  • 대신 @Query 를 사용해서 리파지토리 메소드에 쿼리를 직접 정의한다.
  • 실제 시점에 오류를 잡아주는 장점은 있다. 

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

메서드에 JPQL 쿼리 작성

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);
}
  • @org.springframework.data.jpa.repository.Query 어노테이션을 사용
  • 실행할 메서드에 정적 쿼리를 직접 작성하므로 이름 없는 Named 쿼리라 할 수 있음
  • JPA Named 쿼리처럼 애플리케이션 실행 시점에 문법 오류를 발견할 수 있음(매우 큰 장점!)
  • 실무에서는 메소드 이름으로 쿼리 생성 기능은 파라미터가 증가하면 메서드 이름이 매우 지저분해진다.
  • 따라서 @Query 기능을 자주 사용하게 된다.

4. @Query, 값, DTO 조회하기

1. 단순히 값 하나를 조회

@Query("select m.username from Member m")
List<String> findUsernameList();
  • JPA 값 타입( @Embedded )도 이 방식으로 조회할 수 있다.

2. DTO로 직접 조회

@Data
public class MemberDto {

    private Long id;
    private String username;
    private String teamName;

    public MemberDto(Long id, String username, String teamName) {
        this.id = id;
        this.username = username;
        this.teamName = teamName;
    }
}
@Query("select new study.datajpa.dto.MemberDto(m.id, m.username, t.name) " +
 "from Member m join m.team t")
List<MemberDto> findMemberDto();
  • DTO로 직접 조회 하려면 JPA의 new 명령어를 사용해야 한다. 
  • 그리고 다음과 같이 생성자가 맞는 DTO가 필요하다. (JPA와 사용방식이 동일하다.)

5. 파라미터 바인딩

select m from Member m where m.username = ?0 //위치 기반
select m from Member m where m.username = :name //이름 기반
  • 위치 기반
  • 이름 기반

1. 파라미터 바인딩

public interface MemberRepository extends JpaRepository<Member, Long> {
    @Query("select m from Member m where m.username = :name")
    Member findMembers(@Param("name") String username); 
}

 

2. 컬렉션 파라미터 바인딩

@Query("select m from Member m where m.username in :names")
List<Member> findByNames(@Param("names") List<String> names);
@Test
public void findByNames() {
    Member member1 = new Member("AAA", 10);
    Member member2 = new Member("BBB", 20);
    memberRepository.save(member1);
    memberRepository.save(member2);

    List<Member> result = memberRepository.findByNames(Arrays.asList("AAA", "BBB"));
    for (Member member : result) {
        System.out.println("member = " + member);
    }
}
  • Collection 타입으로 in절 지원

6. 반환 타입

스프링 데이터 JPA는 유연한 반환 타입 지원한다.

List<Member> findByUsername(String name); //컬렉션
Member findByUsername(String name); //단건
Optional<Member> findByUsername(String name); //단건 Optional

 

조회 결과가 많거나 없으면?

  • 컬렉션
    • 결과 없음: 빈 컬렉션 반환(Null이 아니라 빈 컬렉션을 반환 하므로 주의!)
  • 단건 조회
    • 결과 없음: null 반환 
    • 결과가 2건 이상: javax.persistence.NonUniqueResultException 예외 발생(Optional이든 아니든 터짐)

단건으로 지정한 메서드를 호출하면 스프링 데이터 JPA는 내부에서 JPQL의 Query.getSingleResult() 메서드를 호출한다.

이 메서드를 호출했을 때 조회 결과가 없으면 NoResultException 예외가 발생하는데 개발자 입장에서 다루기가 상당히 불편하다. 

스프링 데이터 JPA는 단건을 조회할 때 이 예외가 발생하면 예외를 무시하고 대신에 null 을 반환한다.

 

리턴 타입들 참고 링크: https://docs.spring.io/spring-data/jpa/docs/current/reference/html/ #repository-query-return-types


 

 

'JPA' 카테고리의 다른 글

[JPA] 컬렉션 조회 최적화  (0) 2025.01.10
[JPA] 지연 로딩과 조회 성능 최적화  (0) 2025.01.07
[Spring Data JPA] 공통 인터페이스 기능  (0) 2024.11.11
[JPA 사용법] JPQL  (0) 2024.10.30
[JPA 사용법] 값 타입  (0) 2024.10.30