리포지토리를 생성하기 앞서 도메인은 다음과 같다.
public class Item {
private @Id String id;
private String name;
private double price;
private Item(){}
public Item(String name, double price) {
this.name = name;
this.price = price;
}
public String getId() {
return id;
}
}
블로킹API인 JPA를 사용하지 않지만 JPA와 매우 유사한 인터페이스가 정의되어 있다.
ReactiveCrudRepository 인터페이스를 상속하면 된다. 주석 내용은 기본적으로 제공하는 메서드이다. JPA와 거의 동일하게 제공한다는 것을 알 수 있다.
public interface ItemRepository extends ReactiveCrudRepository<Item, String> {
/**
* save(), saveAll()
* findById(), findAll(), findAllById()
* existsById(), count()
* deleteById(), delete(), deleteAll()
*/
}
추가적인 쿼리를 정의해야 한다면 JPA와 동일하게 키워드를 사용해 기능을 구현한다. 몽고디비나 JPA 또는 그 어떤 쿼리문도 직접 작성할 필요가 없다. 메서드 이름 규칙만 잘 활용한다면 스프링 데이터가 자동으로 만들어준다.
public interface ItemRepository extends ReactiveCrudRepository<Item, String> {
Flux<Item> findByNameContaining(String partialName);
}
쿼리문 자동 생성 메서드로 충분하지 않을 때
- @Query 애너테이션 사용하여 직접 쿼리문 작성하기
(몽고디비 쿼리는 처음 써본다...)
@Query("{'name': ?0, 'age': ?1}")
Flux<Item> findItemForCustomerMonthlyReport(String name, int age);
@Query(sort="{'age': -1}")
Flux<Item> findSortedStuffForWeeklyR
- Example 쿼리 사용하기
여러 조건을 조립해서 스프링 데이터에 전달하면, 스프링 데이터는 필요한 쿼리문을 만들어준다. 조건이 추가될 때마다 계속 복잡해지는 코드를 유지 관리할 필요가 없다
(findByNameContainingOrDescriptionContainingAllIgnoreCase(String partialName, String partialDesc) 과 같은...)
public interface ItemByExampleRepository extends ReactiveQueryByExampleExecutor<Item> {
}
Example 쿼리를 사용하려면 ReactiveQueryByExampleExecutor<T>를 상속받아야 한다.
Flux<Item> searchByExample(String name, String description, boolean useAnd){
Item item = new Item(description, 0.0);
ExampleMatcher matcher = (useAnd
? ExampleMatcher.matchingAll()
: ExampleMatcher.matchingAny())
.withStringMatcher(ExampleMatcher.StringMatcher.CONTAINING) // 1
.withIgnoreCase() // 2
.withIgnorePaths("price"); // 3
Example<Item> probe = Example.of(item, matcher); // 4
return exampleRepository.findAll(probe); // 5
}
복잡한 요구 조건을 Example 쿼리로 구현한 코드이다.
- StringMatcher.CONTAINING을 사용해서 부분 일치 검색을 수행한다.
- 대소문자를 구분하지 않는다.
- ExampleMatcher는 기본적으로 null 필드를 무시하지만, 기본 타입인 double(price 필드) 에는 null이 올 수 없으므로 price 필드가 무시되도록 명시적으로 지정한다.
- Item 객체와 matcher를 사용해 Example.of(...)로 감싸서 Example을 생성한다.
- 쿼리를 실행한다.
가벼워 보이지만 기능은 막강하다. 주어진 요구사항을 모두 충족할 뿐만 아니라 향후 검색 조건 필드가 추가되더라도 어렵지 않게 수용할 수 있다.
- 평문형 연산 (Fluent Operation)
Flux<Item> searchByFluentExample(String name, String description){
return fluentOperations.query(Item.class)
.matching(query(where("TV tray").is(name).and("Smurf").is(description)))
.all();
}
'Develop > Spring˙Spring Boot' 카테고리의 다른 글
[Spring Boot] 스프링 부트의 메시징 솔루션 (0) | 2023.03.16 |
---|---|
[Spring Reactive] 리액티브 테스트 작성하기 (0) | 2023.03.13 |
[Spring Reactive] 리액티브 데이터베이스 저장 요건 (0) | 2023.03.10 |
[Spring Reactive] WebFlux를 사용해 Reactive Spring Boot 구성해보기 (0) | 2023.03.08 |
[Spring] 컴포넌트 탐색, 의존관계 주입과 서비스 로케이터 (0) | 2023.03.07 |