Spring & Spring boot

[Spring Boot] JPA - 5. JPQL Query Method 쿼리 메서드

jh4dev 2024. 8. 10. 22:43
<목차>

1. JPQL 쿼리 메서드
2. Subject / Predicate Keywords
3. Sort / Paging

 

[JPQL 쿼리 메서드]

JPQL 은, JPA Query Language 의 약자로, JPA에서 사용할 수 있는 쿼리를 의미한다.

JPQL 은 Entity 객체를 대상으로 수행하는 쿼리이기 때문에, 매핑된 엔티티의 이름과 필드의 이름을 사용한다.

 

Repository 는 JpaRepository 를 상속받는 것 만으로도 다양한 기본 쿼리 메서드를 사용할 수 있다.

하지만, 기본 메서드의 경우 엔티티의 Id (PK, 식별자) 기반으로 제공되기 때문에, 결국 별도의 메서드를 정의하여 사용하는 경우가 많으며, 이를 '쿼리 메서드' 라고 한다.

 

쿼리 메서드는, 크게 동작을 결정하는 주제(Subject)서술어(Predicate)로 구분한다.

  • Subject
    • find ~ By / exists ~ By 와 같은 키워드로 주제를 정한다.
  • Predicate
    • By 를 기점으로 이후 부분이 서술어이며, 검색 및 정렬 조건을 지정한다.
    • 엔티티의 속성(필드)로 정의할 수 있으며, And / Or 을 사용하여 조건을 확장할 수 있다.
      ex) findByNameAndAge(String name, long age)

[Subject Keywords]

쿼리 메서드의 주제 영역에 사용할 수 있는 주요 키워드

  • 조회 쿼리 메서드 키워드
    • 조건에 맞는 데이터를 조회하는 키워드로, Collection / Stream 의 하위 타입을 리턴 타입으로 설정할 수 있다.

 

  • find ~ By : 가장 일반적으로 사용되는 키워드로, 조건에 맞는 데이터를 조회한다.
    동일한 역할을 하는 키워드로, read ~ By / get ~ By / query ~ By / search ~ By / stream ~ By 등이 있다.
    // 조회 키워드를 활용한 쿼리 메서드
    Optional<Product> findByNumber(Long number);
    List<Product> findAllByName(String name);
    Product queryByNumber(Long number);

 

  • exists ~ By : 특정 데이터가 존재하는지 확인하는 키워드로, boolean 타입으로 리턴한다.
    boolean existsByNumber(Long number);

 

  • count ~ By : 쿼리를 수행한 후, 결과로 나온 Row 개수를 리턴한다.
    long countByName(String name);

 

  • delete ~ By / remove ~ By : 삭제 쿼리를 수행한 후, 삭제 횟수를 리턴한다. (void 가능)
    long deleteByNumber(long number); // 삭제된 Row 수 리턴
    void removeByNumber(long number); // void 로도 처리 가능

 

  • ~ First{number} ~ / ~ Top{number} ~ : 쿼리로 조회된 결과값의 개수를 제한하는 키워드로, 흔히 알고 있는 limit / top 등의 역할을 수행한다.
    List<Product> findFirst5ByName(String name);
    List<Product> findTop5ByName(String name);

 


[Predicate Keyword]

쿼리 메서드의 서술어 영역에 사용할 수 있는 주요 키워드

  • Is / Equals : 값의 일치를 조건으로 사용하는 조건자 키워드
    생략 가능하다.
    // Is, Equals > 일치하는 데이터를 찾는다.
    // findByNumber() 와 동일하게 동작한다
    Product findByNumberIs(Long number);
    Product findByNumberEquals(Long number);

 

  • (Is)Not : 값의 불일치를 조건으로 사용하는 조건자 키워드
    // (Is)Not > 불일치하는 데이터를 찾는다.
    Product findByNumberIsNot(Long number);
    Product findByNumberNot(Long number);

 

  • (Is)Null / NotNull : Null 체크하는 조건자 키워드
    SQL 에서의 IS NULL / IS NOT NULL 과 동일
    // (Is)Null, (Is)NotNull
    List<Product> findByUpdatedAtNull();
    List<Product> findByUpdatedAtIsNull();
    List<Product> findByUpdatedAtNotNull();
    List<Product> findByUpdatedAtIsNotNull();

 

  • (Is)True / False : boolean 타입으로 지정된 컬럼 값을 확인하는 키워드
    // (Is)True, (Is)False
    Product findByIsActiveTrue();
    Product findByIsActiveIsTrue();
    Product findByIsActiveFalse();
    Product findByIsActiveIsFalse();

 

  • And / Or : 여러 조건을 연결할 때 사용
    // And, Or > 조건을 묶을 때 사용
    Product findByNumberAndName(Long number, String nme);
    Product findByNumberOrName(Long number, String name);

 

  • (Is)GreaterThan / LessThan / Between : 숫자 또는 datetime 타입의 컬럼을 대상으로 비교 연산에 사용할 수 있는 키워드
    • 초과 : GreaterThan
    • 이상 : GreaterThanEqual
    • 미만 : LessThan
    • 이하 : LessThanEqual
    // (Is)GreaterThan, (Is)LessThan, (Is)Between + Equal
    // GreaterThan
    List<Product> findByPriceIsGreaterThan(Long price);     // Price 초과
    List<Product> findByPriceGreaterThan(Long price);       // Price 초과
    List<Product> findByPriceGreaterThanEqual(Long Price);  // Price 이상
    // LessThan
    List<Product> findByPriceIsLessThan(Long price);        // Price 미만
    List<Product> findByPriceLessThan(Long price);          // Price 미만
    List<Product> findByPriceLessThanEqual(Long Price);     // Price 이하
    // Between
    List<Product> findByPriceBetween(Long lowPrice, Long highPrice);
    List<Product> findByPriceIsBetween(Long lowPrice, Long highPrice);

 

  • (Is)StartingWith / EndingWith / Containing / Like
    startsWith / EndsWith / Contains
    • SQL 에서의 LIKE 역할을 하는 키워드이다.
    • StartingWith(StartsWith) : 문자열 앞에 '%' 가 배치된다.
    • EndingWith(EndsWith) : 문자열 뒤에 '%' 가 배치된다.
    • Contining(Contains) : 문자열 앞 뒤에 '%' 가 배치된다.
    • Like : '%' 를 원하는 위치에 배치시킨 문자열 자체가 파라미터로 전달되어야 한다.
    // (Is)StartsWith / StartingWith, (Is)EndsWith / EndingWith, (Is)Contains / Containing, (Is)Like
    // StartsWith / StartingWith
    List<Product> findByNameStartsWith(String name);
    List<Product> findByNameStartingWith(String name);
    List<Product> findByNameIsStartingWith(String name);

    // EndsWith / EndingWith
    List<Product> findByNameEndsWith(String name);
    List<Product> findByNameEndingWith(String name);
    List<Product> findByNameIsEndingWith(String name);

    // Contains / Containing
    List<Product> findByNameContains(String name);
    List<Product> findByNameContaining(String name);
    List<Product> findByNameIsContaining(String name);

    // Like > 파라미터의 원하는 위치에 '%' 를 입력하여 호출해야 함
    List<Product> findByNameLike(String name);
    List<Product> findByNameIsLike(String name);

 

  • OrderBy : 일반적인 쿼리문과 동일하게 OrderBy 구문을 사용한다.
    기본 쿼리메서드의 뒷부분에 OrderBy ~ Asc / Desc 를 입력해주면 되며, 둘 이상의 정렬 조건을 부여해야 한다면 우선순위대로 나열해주면 된다.(And 와 같은 키워드는 불필요)
    // 정렬
    List<Product> findByNameOrderByNumberAsc(String name);
    List<Product> findByNameOrderByNumberDesc(String name);
    List<Product> findByNameOrderByPriceAscStockDesc(String name);  
    // 정렬 컬럼 간 And 연결하지 않고 나열
    List<Product> findByName(String name, Sort sort);

[Sort / Paging]

  • Sort
    정렬 시, OrderBy 키워드를 통해 정렬하는 방법은, 대상 필드의 수가 많아지면 메서드명이 길어지며 가독성이 떨어지게 될 수 있기 때문에, Sort 객체를 사용하여 정렬하는 방법도 알아보자.

1. Sort 객체를 파리미터로 받는 메서드 정의

    List<Product> findByName(String name, Sort sort);

 

2. 메서드 호출 시, Sort.by() 사용하여 정렬 조건 설정

    List<Product> productList = productRepository.findByName("펜", Sort.by(Order.asc("price"), Order.desc("stock")));

 

  • Paging
    JPA에서는 페이징 처리를 위해 Page와 Pageabl을 사용한다.
    페이징을 위한 쿼리 메서드는, 리턴 타입으로 Page을 사용하고, 파라미터는 Pageable 타입의 객체를 정의한다.

1. 페이징 처리를 위한 쿼리 메서드 정의

    Page<Product> findByName(String name, Pageable pageable);

 

 

2. 메서드 호출 시, PageRequest.of() 사용하여 페이징 조건 설정

    Page<Product> productPage = productRepository.findByName("펜", PageRequest.of(0, 2));
  • PageRequest는 Pageable 의 구현체
  • PageRequest.of()
    • of(int page, int size) -> (페이지 번호, 페이지 당 데이터 수)
    • of(int page, int size, Sort sort) -> (페이지 번호, 페이지 당 데이터 수, 정렬 조건)

 


관련 소스 
[Github]

 

spring-boot-study/advjpa at main · jh4dev/spring-boot-study

스프링 부트 스터디. Contribute to jh4dev/spring-boot-study development by creating an account on GitHub.

github.com

 


[참고 도서]

 

<스프링 부트 핵심 가이드>
저자 : 장정우

출판사 : 위키북스