Spring Data JPA-Abfrage anhand eines Beispiels

1. Einleitung

In diesem Lernprogramm erfahren Sie, wie Sie Daten mit der Spring Data Query by Example-API abfragen .

Zunächst definieren wir das Schema der Daten, die wir abfragen möchten. Als Nächstes untersuchen wir einige der relevanten Klassen aus Spring Data. Und dann werden wir einige Beispiele durchgehen.

Lass uns anfangen!

2. Die Testdaten

Unsere Testdaten sind eine Liste der Passagiernamen sowie des von ihnen besetzten Sitzplatzes.

Vorname Familienname, Nachname Sitznummer
Jill Schmied 50
Vorabend Jackson 94
Fred Bloggs 22
Ricki Bobbie 36
Siya Kolisi 85

3. Domain

Lassen Sie uns das benötigte Spring Data Repository erstellen und unsere Domänenklasse und unseren ID-Typ angeben.

Zunächst haben wir unseren Passagier als JPA-Einheit modelliert :

@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. }

Anstatt JPA zu verwenden, hätten wir es als eine andere Abstraktion modellieren können.

4. Abfrage nach Beispiel-API

Schauen wir uns zunächst die JpaRepository- Oberfläche an. Wie wir sehen können, wird die QueryByExampleExecutor- Schnittstelle erweitert, um die Abfrage anhand eines Beispiels zu unterstützen:

public interface JpaRepository extends PagingAndSortingRepository, QueryByExampleExecutor {}

Diese Schnittstelle führt weitere Varianten der find () -Methode ein, die wir aus Spring Data kennen. Jede Methode akzeptiert jedoch auch eine Instanz von Beispiel :

public interface QueryByExampleExecutor {  Optional findOne(Example var1);  Iterable findAll(Example var1);  Iterable findAll(Example var1, Sort var2);  Page findAll(Example var1, Pageable var2);  long count(Example var1);  boolean exists(Example var1); }

Zweitens stellt die Beispielschnittstelle Methoden für den Zugriff auf die Sonde und den ExampleMatcher bereit .

Es ist wichtig zu erkennen, dass die Sonde die Instanz unserer Entität ist :

public interface Example { static  org.springframework.data.domain.Example of(T probe) { return new TypedExample(probe, ExampleMatcher.matching()); } static  org.springframework.data.domain.Example of(T probe, ExampleMatcher matcher) { return new TypedExample(probe, matcher); } T getProbe(); ExampleMatcher getMatcher(); default Class getProbeType() { return ProxyUtils.getUserClass(this.getProbe().getClass()); } }

Zusammengefasst unsere Sonde und unsere ExampleMatcher zusammen unsere Abfrage angeben.

5. Einschränkungen

Wie alle Dinge weist die API "Abfrage nach Beispiel" einige Einschränkungen auf. Zum Beispiel:

  • Verschachtelungs- und Gruppierungsanweisungen werden beispielsweise nicht unterstützt: ( Vorname =? 0 und Nachname =? 1) oder Sitznummer =? 2
  • Der String-Abgleich umfasst nur exakte, unabhängig von Groß- und Kleinschreibung, Starts, Enden, Enthaltens und reguläre Ausdrücke
  • Alle Typen außer String stimmen nur genau überein

Nachdem wir uns mit der API und ihren Einschränkungen ein wenig vertraut gemacht haben, wollen wir uns einige Beispiele ansehen.

6. Beispiele

6.1. Groß- und Kleinschreibung beachten

Beginnen wir mit einem einfachen Beispiel und sprechen über das Standardverhalten:

@Test public void givenPassengers_whenFindByExample_thenExpectedReturned() { Example example = Example.of(Passenger.from("Fred", "Bloggs", null)); Optional actual = repository.findOne(example); assertTrue(actual.isPresent()); assertEquals(Passenger.from("Fred", "Bloggs", 22), actual.get()); }

Insbesondere erstellt die statische Example.of () -Methode ein Beispiel mit ExampleMatcher.matching () .

Mit anderen Worten, eine genaue Übereinstimmung wird für alle Nicht-Null-Eigenschaften des Passagiers durchgeführt . Bei der Übereinstimmung wird daher bei String- Eigenschaften zwischen Groß- und Kleinschreibung unterschieden .

Es wäre jedoch nicht allzu nützlich, wenn wir nur eine exakte Übereinstimmung für alle Nicht-Null-Eigenschaften tun könnten.

Hier kommt der ExampleMatcher ins Spiel . Indem wir unseren eigenen ExampleMatcher erstellen , können wir das Verhalten an unsere Bedürfnisse anpassen.

6.2. Groß- und Kleinschreibung wird nicht berücksichtigt

Schauen wir uns vor diesem Hintergrund ein anderes Beispiel an, diesmal mit withIgnoreCase () , um eine Übereinstimmung ohne Berücksichtigung der Groß- und Kleinschreibung zu erzielen:

@Test public void givenPassengers_whenFindByExampleCaseInsensitiveMatcher_thenExpectedReturned() { ExampleMatcher caseInsensitiveExampleMatcher = ExampleMatcher.matchingAll().withIgnoreCase(); Example example = Example.of(Passenger.from("fred", "bloggs", null), caseInsensitiveExampleMatcher); Optional actual = repository.findOne(example); assertTrue(actual.isPresent()); assertEquals(Passenger.from("Fred", "Bloggs", 22), actual.get()); }

Beachten Sie in diesem Beispiel, dass wir zuerst ExampleMatcher.matchingAll () aufgerufen haben - es hat dasselbe Verhalten wie ExampleMatcher.matching () , das wir im vorherigen Beispiel verwendet haben.

6.3. Benutzerdefiniertes Matching

Mit ExampleMatcher.matchingAny () können wir auch das Verhalten unseres Matchers pro Eigenschaft optimieren und jede Eigenschaft abgleichen :

@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 example = Example.of(Passenger.from("e", "s", null), customExampleMatcher); List passengers = repository.findAll(example); assertThat(passengers, contains(jill, eve, fred, siya)); assertThat(passengers, not(contains(ricki))); }

6.4. Eigenschaften ignorieren

Andererseits möchten wir möglicherweise auch nur eine Teilmenge unserer Eigenschaften abfragen .

Dies erreichen wir, indem wir einige Eigenschaften mit ExampleMatcher.ignorePaths (String… Pfade) ignorieren :

@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 example = Example.of(Passenger.from(null, "b", null), ignoringExampleMatcher); List 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. Fazit

In diesem Artikel haben wir gezeigt, wie die API "Abfrage nach Beispiel" verwendet wird.

Wir haben gezeigt, wie Example und ExampleMatcher zusammen mit der QueryByExampleExecutor- Schnittstelle verwendet werden, um eine Tabelle mithilfe einer Beispieldateninstanz abzufragen.

Abschließend finden Sie den Code auf GitHub.