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

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

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

[ 메서드 이름으로 쿼리 생성 ]

1. 순수 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();
}

테스트 실행 결과


2. 스프링 데이터 JPA

public interface MemberRepository extends JpaRepository<Member, Long> {
    List<Member> findByUsernameAndAgeGreaterThan(String username, int age);
}

스프링 데이터 JPA는 메서드 이름을 분석해서 JPQL을 생성하고 실행한다.
→ 그래서 필드명을 틀리면 안된다! (공식 문서 참고하기)

📌 쿼리 메서드 필터 조건

스프링 데이터 JPA 공식 문서 참고

📌 스프링 데이터 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 NamedQuery ]

실무에서 거의 쓸 일 없으므로 알아만 두기!

1. 순수 JPA 이용

1) @NamedQuery 어노테이션으로 Named 쿼리 정의

@NamedQuery(
        name="Member.findByUsername",
        query="select m from Member m where m.username = :username"
)
public class Member { ... }
  • 엔티티에 @NamedQuery를 정의한다.

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

public List<Member> findByUsername(String username) {
    return em.createNamedQuery("Member.findByUsername", Member.class)
        .setParameter("username", username)
        .getResultList();
}

테스트 실행 결과

→ 실행은 잘 되지만 귀찮다😥 스프링 데이터 JPA를 이용해보자!


2. 스프링 데이터 JPA 이용

1) 스프링 데이터 JPANamedQuery 사용

@Query(name = "Member.findByUsername")
List<Member> findByUsername(@Param("username") String username);
  • @Query를 생략해도 된다!
    → 메서드 이름만으로 NamedQuery 호출이 가능하다.

2) 스프링 데이터 JPANamed 쿼리 호출

스프링 데이터 JPA는 선언한 <도메인 클래스 + . + 메서드 이름> 으로 Named 쿼리를 찾아서 실행한다.

만약 해당하는 쿼리가 없으면 메서드 이름으로 쿼리 생성 전략을 사용한다.

테스트 실행 결과


[ @Query, 리포지토리 메서드에 쿼리 정의하기 ]

실무에서 많이 쓰이는 방법이다!

메서드에 JPQL 쿼리 작성

@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);
  • 실행할 메서드에 정적 쿼리를 직접 작성하므로 이름 없는 Named 쿼리라고 할 수 있다.
  • JPA Named 쿼리처럼 어플리케이션 실행 시점에 문법 오류를 발견할 수 있다!

참고: 동적쿼리는 Querydsl를 써야 한다.


[ @Query, 값, DTO 조회하기 ]

지금까지는 엔티티만 조회했는데,
이번에는 단순한 값이나 DTO를 조회하는 방법에 대해 알아보자!

1. 단순한 값 하나 조회

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

테스트 실행 결과


2. DTO로 직접 조회

@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도 필요하다!

MemberDto.java

@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;
    }
}

테스트 실행 결과


[ 파라미터 바인딩 ]

파라미터 바인딩은 위치 기반, 이름 기반 두 가지가 있다.

1. 파라미터 바인딩

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

위치 기반은 가독성도 떨어지고, 유지보수하기도 어렵다. 그러므로 이름 기반을 사용하자!

@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") Collection<String> names);
  • Collection 타입으로 in절 지원

좋은 웹페이지 즐겨찾기