1. 소개
이번 예제에서는 Spring Data Query by Example API로 데이터 를 쿼리하는 방법을 알아보겠습니다 .
먼저 쿼리할 데이터의 스키마를 정의합니다. 다음으로 Spring Data의 몇 가지 관련 클래스를 살펴보겠습니다. 그런 다음 몇 가지 예를 살펴보겠습니다.
시작하자!
2. 테스트 데이터
우리의 테스트 데이터는 승객 이름 List과 그들이 차지한 좌석입니다.
이름 | 성 | 좌석 번호 |
---|---|---|
처녀 | 스미스 | 50 |
이브 | 잭슨 | 94 |
프레드 | 블로그 | 22 |
리키 | 바비 | 36 |
시야어 | 콜리시 | 85 |
3. 도메인
필요한 Spring Data Repository 를 생성 하고 도메인 클래스와 id 유형을 제공합시다.
우선 Passenger 를 JPA 엔터티로 모델링했습니다.
@Entity
class Passenger {
@Id
@GeneratedValue
@Column(nullable = false)
private Long id;
@Basic(optional = false)
@Column(nullable = false)
private String firstName;
@Basic(optional = false)
@Column(nullable = false)
private String lastName;
@Basic(optional = false)
@Column(nullable = false)
private int seatNumber;
// constructor, getters etc.
}
JPA를 사용하는 대신 다른 추상화로 모델링할 수 있었습니다.
4. 예제 API로 쿼리하기
먼저 JpaRepository 인터페이스 를 살펴보겠습니다 . 보시다시피 QueryByExampleExecutor 인터페이스를 확장하여 예제별 쿼리를 지원합니다.
public interface JpaRepository<T, ID>
extends PagingAndSortingRepository<T, ID>, QueryByExampleExecutor<T> {}
이 인터페이스는 Spring Data에서 우리에게 친숙한 find() 메서드의 더 많은 변형을 도입합니다. 그러나 각 메서드는 Example 의 인스턴스도 허용합니다 .
public interface QueryByExampleExecutor<T> {
<S extends T> Optional<S> findOne(Example<S> var1);
<S extends T> Iterable<S> findAll(Example<S> var1);
<S extends T> Iterable<S> findAll(Example<S> var1, Sort var2);
<S extends T> Page<S> findAll(Example<S> var1, Pageable var2);
<S extends T> long count(Example<S> var1);
<S extends T> boolean exists(Example<S> var1);
}
둘째, Example 인터페이스는 프로브 및 ExampleMatcher 에 액세스하는 메서드를 노출합니다 .
프로브 가 Entity 의 인스턴스 임을 인식하는 것이 중요합니다 .
public interface Example<T> {
static <T> org.springframework.data.domain.Example<T> of(T probe) {
return new TypedExample(probe, ExampleMatcher.matching());
}
static <T> org.springframework.data.domain.Example<T> of(T probe, ExampleMatcher matcher) {
return new TypedExample(probe, matcher);
}
T getProbe();
ExampleMatcher getMatcher();
default Class<T> getProbeType() {
return ProxyUtils.getUserClass(this.getProbe().getClass());
}
}
요약하면 프로브 와 ExampleMatcher 가 함께 쿼리를 지정합니다.
5. 제한 사항
모든 것과 마찬가지로 Query by Example API에는 몇 가지 제한 사항이 있습니다. 예를 들어:
- 중첩 및 그룹화 문은 지원되지 않습니다. 예: ( firstName = ?0 및 lastName = ?1) 또는 seatNumber = ?2
- 문자열 일치에는 정확함, 대소문자 구분 없음, 시작, 끝, 포함 및 정규식만 포함됩니다.
- 문자열 이외의 모든 유형 은 정확히 일치만 가능합니다.
이제 API와 해당 제한 사항에 대해 좀 더 잘 알고 있으므로 몇 가지 예를 살펴보겠습니다.
6. 예시
6.1. 대소문자 구분 매칭
간단한 예부터 시작하여 기본 동작에 대해 이야기해 보겠습니다.
@Test
public void givenPassengers_whenFindByExample_thenExpectedReturned() {
Example<Passenger> example = Example.of(Passenger.from("Fred", "Bloggs", null));
Optional<Passenger> actual = repository.findOne(example);
assertTrue(actual.isPresent());
assertEquals(Passenger.from("Fred", "Bloggs", 22), actual.get());
}
특히 정적 Example.of() 메서드는 ExampleMatcher.matching() 을 사용하여 예제 를 빌드합니다 .
즉, Passenger 의 null이 아닌 모든 속성에서 정확한 일치가 수행 됩니다 . 따라서 일치는 문자열 속성 에서 대/소문자를 구분 합니다.
그러나 우리가 할 수 있는 모든 것이 null이 아닌 속성에 대해 정확히 일치하는 것이라면 그다지 유용하지 않을 것입니다.
여기에서 ExampleMatcher 가 등장합니다. 자체 ExampleMatcher 를 구축 하여 필요에 맞게 동작을 사용자 지정할 수 있습니다.
6.2. 대소문자를 구분하지 않는 일치
이를 염두에 두고 이번에는 withIgnoreCase() 를 사용하여 대소문자를 구분하지 않는 일치를 달성하는 또 다른 예를 살펴보겠습니다.
@Test
public void givenPassengers_whenFindByExampleCaseInsensitiveMatcher_thenExpectedReturned() {
ExampleMatcher caseInsensitiveExampleMatcher = ExampleMatcher.matchingAll().withIgnoreCase();
Example<Passenger> example = Example.of(Passenger.from("fred", "bloggs", null),
caseInsensitiveExampleMatcher);
Optional<Passenger> actual = repository.findOne(example);
assertTrue(actual.isPresent());
assertEquals(Passenger.from("Fred", "Bloggs", 22), actual.get());
}
이 예제에서는 먼저 ExampleMatcher.matchingAll() 을 호출했습니다 . 이전 예제에서 사용한 ExampleMatcher.matching() 과 동작이 동일 합니다.
6.3. Custom 매칭
속성별로 매처의 동작을 조정하고 ExampleMatcher.matchingAny() 를 사용하여 모든 속성을 일치 시킬 수도 있습니다 .
@Test
public void givenPassengers_whenFindByExampleCustomMatcher_thenExpectedReturned() {
Passenger jill = Passenger.from("Jill", "Smith", 50);
Passenger eve = Passenger.from("Eve", "Jackson", 95);
Passenger fred = Passenger.from("Fred", "Bloggs", 22);
Passenger siya = Passenger.from("Siya", "Kolisi", 85);
Passenger ricki = Passenger.from("Ricki", "Bobbie", 36);
ExampleMatcher customExampleMatcher = ExampleMatcher.matchingAny()
.withMatcher("firstName", ExampleMatcher.GenericPropertyMatchers.contains().ignoreCase())
.withMatcher("lastName", ExampleMatcher.GenericPropertyMatchers.contains().ignoreCase());
Example<Passenger> example = Example.of(Passenger.from("e", "s", null), customExampleMatcher);
List<Passenger> passengers = repository.findAll(example);
assertThat(passengers, contains(jill, eve, fred, siya));
assertThat(passengers, not(contains(ricki)));
}
6.4. 속성 무시
반면에 속성의 하위 집합에 대해서만 쿼리를 원할 수도 있습니다 .
ExampleMatcher.ignorePaths(String… paths) 를 사용하여 일부 속성을 무시함으로써 이를 달성합니다 .
@Test
public void givenPassengers_whenFindByIgnoringMatcher_thenExpectedReturned() {
Passenger jill = Passenger.from("Jill", "Smith", 50);
Passenger eve = Passenger.from("Eve", "Jackson", 95);
Passenger fred = Passenger.from("Fred", "Bloggs", 22);
Passenger siya = Passenger.from("Siya", "Kolisi", 85);
Passenger ricki = Passenger.from("Ricki", "Bobbie", 36);
ExampleMatcher ignoringExampleMatcher = ExampleMatcher.matchingAny()
.withMatcher("lastName", ExampleMatcher.GenericPropertyMatchers.startsWith().ignoreCase())
.withIgnorePaths("firstName", "seatNumber");
Example<Passenger> example = Example.of(Passenger.from(null, "b", null), ignoringExampleMatcher);
List<Passenger> passengers = repository.findAll(example);
assertThat(passengers, contains(fred, ricki));
assertThat(passengers, not(contains(jill));
assertThat(passengers, not(contains(eve));
assertThat(passengers, not(contains(siya));
}
7. 결론
이 기사에서는 Query by Example API를 사용하는 방법을 설명했습니다.
예제 데이터 인스턴스를 사용하여 테이블을 쿼리하기 위해 QueryByExampleExecutor 인터페이스 와 함께 Example 및 ExampleMatcher 를 사용하는 방법을 시연 했습니다.
결론적 으로 GitHub 에서 코드를 찾을 수 있습니다 .