데이터베이스 쿼리

지금까지 몽고디비를 이용해 데이터를 조회하고 저장했는데, 큰 이점을 확인하지는 못했다.

실제 이커머스에는 데이터베이스에서 id로 조회하는 기능만 있는 것이 아니라 전체 상품을 보여주고 고객에게 상품을 고르게 하는 것이 더 일반적이다.

또한 고객에게 검색어를 입력하게 하고 그에 맞는 상품 목록을 보여주는 기능도 필요하다.

지금부터 검색 기능을 만들어보도록 하겠다.

검색어로 상품 목록을 조회하는 리포지토리 코드이다.

package com.greglturnquist.hackingspringboot.reactive.item;

import org.springframework.data.repository.reactive.ReactiveCrudRepository;
import reactor.core.publisher.Flux;

public interface ItemRepository extends ReactiveCrudRepository<Item, String> {
    Flux<Item> findByNameContaining(String partialName);
}

이 리포지토리는 고객이 입력한 검색어가 이름에 포함된 상품을 반환하는 메소드를 가지고 있다.

메소드 이름에 포함된 containing이라는 일종의 키워드가 그 기능을 담당하며 개발자가 관련 구현 코드를 직접 구현할 필요는 없다.

스프링 데이터에서 정한 메소드 이름 규칙을 사용해서 리포지토리 메소드를 정의하면 일반적으로 필요한 쿼리문의 80% 정도를 직접 코드나 SQL 문을 작성하지 않고 자동으로 만들어낼 수 있다.

Item 객체에 description 등 몇 가지 필드가 더 있다고 가정하고 스프링 데이터의 메소드 이름 규칙을 알아보자.

package com.greglturnquist.hackingspringboot.reactive.item;

import org.springframework.data.annotation.Id;

import java.awt.*;
import java.util.Date;

public class Item {
    private @Id String id;
    private String name;
    private String description;
    private double price;
    private String distributionRegion;
    private Date releaseDate;
    private int availableUnits;
    private Point location;
    private boolean active;

    private Item() {}
}

상품의 여러 속성을 기준으로 쿼리문을 자동으로 만들어주는 메소드 이름 규칙은 다음과 같다.

쿼리 메소드 설명
findByDescription(…) description 값이 일치하는 데이터 질의
findByNameAndDescription(…) name값과 description 값이 모두 일치하는 데이터 질의
findByNameAndDistributorRegion(…) name값과 distributorRegion 값이 모두 일치하는 데이터 질의
findTop10ByName(…) 또는
findFirst10ByName(…) name 값이 일치하는 첫 10개의 데이터 질의
findByNameIgnoreCase(…) name 값이 대소문자 구분 없이 일치하는 데이터 질의
findByNameAndDescriptionAllIgnoreCase(…) name값과 description 값 모두 대소문자 구분 없이 일치하는 데이터 질의
findByNameOrderByDescriptionAsc(…) name 값이 일치하는 데이터를 description 값 기준 오름차순으로 정렬한 데이터 질의
findByReleaseDateBefore(Date date) releaseDate 값이 date보다 이전인 데이터 질의
findByReleaseDateAfter(Date date) releaseDate 값이 date 이후인 데이터 질의
findByAvailableUnitsGreaterThan(int units) availableUnits 값이 units보다 큰 데이터 질의
findByAvailableUnitsGreaterThanEqual(int units) availableUnits 값이 units보다 크거나 같은 데이터 질의
findByAvailableUnitsLessThan(int units) availableUnits 값이 units보다 작은 데이터 질의
findByAvailableUnitsLessThanEqual(int units) availableUnits 값이 units보다 작거나 같은 데이터 질의
findByAvailableUnitsBetween(int from, int to) availableUnits 값이 from과 to 사이에 있는 데이터 질의
findByAvailableUnitsIn(Collection unitss) availableUnits 값이 unitss 컬렉션에 포함돼 있는 데이터 질의
findByAvailableUnitsNotIn(Collection unitss) availableUnits 값이 unitss 컬렉션에 포함돼 있지 않은 데이터 질의
findByNameNotNull() 또는
findByNameIsNotNull() name 값이 null 이 아닌 데이터 질의
findByNameNull() 또는
findByNameIsNull() name 값이 null인 데이터 질의
findByNameLike(String f) name 값이 문자열 f를 포함하는 데이터 질의
findByNameNotLike(String f) 또는
findByNameIsNotLike(String f) name 값이 문자열 f를 포함하지 않는 데이터 질의
findByNameStartingWith(String f) name 값이 문자열 f로 시작하는 데이터 질의
findByNameEndingWith(String f) name 값이 문자열 f로 끝나는 데이터 질의
findByNameNotContaining(String f) name 값이 문자열 f를 포함하지 않는 데이터 질의
findByNameRegex(String patter) name 값이 pattern으로 표현되는 정규 표현식에 해당하는 데이터 질의
findByLocationNear(Point p, Distance max) location 값이 p 지점 기준 max 이내에서 가장 가까운 순서로 정렬된 데이터 질의
findByLocationNear(Point p, Distance min, Distance max) location 값이 p 지점 기준 거리 min 이상 max 이내에서 가장 가까운 순서로 정렬된 데이터 질의
findByLocationWithin(Circle c) location 값이 원 영역 c 안에 포함돼 있는 데이터 질의
findByLocationWithin(Box b) location 값이 직사각형 영역 b 안에 포함돼 있는 데이터 질의
findByActiveIsTrue() active 값이 true인 데이터 질의
findByActiveIsFalse() active 값이 false 인 데이터 질의
findByLocationExists(boolean e) location 속성의 존재 여부 기준으로 데이터 질의

여기에 굵은 글씨로 표시한 키워드는 deleteBy 메소드에서도 동일하게 사용할 수 있다.

<aside> 💡 표의 키워드는 몽고디비 기준이다. 대부분 다른 데이터 스토어에서도 사용할 수 있고, 데이터 스토어별 메소드 키워드는 해당 데이터 스토어용 스프링 데이터 레퍼런스 문서를 참고하면 된다.

</aside>