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 에서 코드를 찾을 수 있습니다 .

Persistence footer banner