Einstieg in Spring Boot, Teil 4 Datenbankabfragen mit Spring Boot

Autor / Redakteur: Dr. Dirk Koller / Stephan Augsten

Grundlegende find-Methoden wie findById() oder findAll() haben wir im vorhergehenden Teil dieser Reihe vorgestellt. In der Praxis sind Datenbankabfragen meist komplexer, Personen wird man beispielsweise mit Namen und Geburtsdatum suchen. Spring Data bietet dazu verschiedene Möglichkeiten, die wir hier vorstellen.

Firmen zum Thema

Spring Data bietet für Datenbank-Abfragen verschiedene Möglichkeiten, die wir in diesem Beitrag vorstellen.
Spring Data bietet für Datenbank-Abfragen verschiedene Möglichkeiten, die wir in diesem Beitrag vorstellen.
(Bild: Spring.io)

Zu Beginn erweitern wir die Testdaten um mehr Attribute zur Verfügung zu haben. Die Entität Person erhält ein Geburtsdatum und einen Status. Getter und Setter werden aus Platzgründen hier nicht aufgeführt:

@Entitypublic class Person {   @Id
   @GeneratedValue(strategy=GenerationType.AUTO)
   private Long id;
   private String firstname;
   private String lastname;
   private LocalDate birthdate;
   private Integer status;
}

Auch die Inserts in data.sql werden entsprechend erweitert:

INSERT INTO person (id, firstname, lastname, birthday, status) VALUES (-1, 'Max', 'Mustermann', '1976-02-21', 0);
INSERT INTO person (id, firstname, lastname, birthday, status) VALUES (-2, 'Erika', 'Mustermann', '1975-09-12', 1);
„Spring Boot“-Tutorial
Bildergalerie mit 15 Bildern

Derived Queries: Abfragen mit Konventionen

Zunächst soll nun nach dem Vornamen einer Person gesucht werden. Dazu wird eine neue Methode findByFirstname ins Repository-Interface eingefügt:

public interface PersonRepository extends CrudRepository<Person, Long>{   List<Person> findByFirstname(String firstname);
}

Diese Methode kann nun beispielsweise im Controller genutzt werden, um passende Einträge in der Datenbank zu suchen:

List<Person> persons = (List<Person>) personRepository.findByFirstname("Max");

Erwartungsgemäß wird als einzige Person Max Mustermann gefunden … aber Moment mal – wir haben doch die Methode noch gar nicht implementiert?! Hier schlägt wieder Spring-Magie zu. Wenn der Methodenname bestimmten Kriterien entspricht, leitet Spring Data JPA aus dem Namen die Funktionalität ab und stellt die Implementierung zur Laufzeit automatisch zur Verfügung.

Diese Abfragen heißen deshalb „derived“, also abgeleitete, Queries. Entscheidend dabei ist es, die Namenskonventionen einzuhalten. Der Methodenname beginnt mit findBy (es gibt ein paar Alternativen wie z.B. readBy). Anschließend werden die im Entity vorhanden Variablennamen, beginnend mit einem Großbuchstaben aufgeführt. Mehrere Suchkriterien werden durch And oder Or getrennt.

Am besten versteht man das wohl anhand eines Beispiels. Um eine Person anhand des Vor- und Nachnamens zu finden kann die folgende Query verwendet werden:

List<Person> findByFirstnameAndLastname(String firstname, String lastname);

Im Standardfall wird dabei ein Vergleich auf Gleichheit mittels equals durchgeführt. Es gibt aber eine Reihe weiterer Operatoren, die sich alternativ verwenden lassen:

  • Like
  • NotLike
  • Containing
  • IgnoreCase
  • Between
  • LessThan
  • GreaterThan
  • IsNull
  • IsNotNull
  • Not

Um etwa alle Personen zu finden, die eine bestimmte Zeichenfolge im Vornamen haben sieht die Methode mit dem Containing-Operator wie folgt aus:

List<Person> findByFirstnameContaining(String str);

Die Suchergebnisse in Derived Queries lassen sich mit Hilfe der Namenskonvention auch Sortieren. Dazu wird einfach der Zusatz OrderBy, gefolgt vom Sortierkriterium und der Sortierrichtung (asc für aufsteigend oder desc für absteigend) angehängt. Personen mit einem bestimmten Nachnamen, aufsteigend nach dem Geburtsdatum sortiert, findet man also folgendermaßen:

List<Person> findByLastnameOrderByBirthdateAsc(String firstname);

Auch eine Begrenzung der Anzahl der Suchergebnisse ist vorgesehen. Dazu wird die Kennzeichnung First, gefolgt von der Anzahl der gewünschten Ergebnisse zwischen find und by gepackt. Im folgenden Beispiel werden nur die ersten beiden Treffer zurückgegeben:

List<Person> findFirst2ByLastnameOrderByBirthdateAsc(String firstname);

@Query: Abfragen selber formulieren

Derived Queries gehören zu den echten Killer-Features von Spring Data, mit ihnen lässt sich eine Menge Zeit und Arbeit sparen. Sie kommen aber an ihre Grenzen, wenn es komplexer wird. Außerdem gibt es ja durchaus Fälle, wo durch den Namen der Methode ein fachlicher Zusammenhang ausgedrückt werden soll, der sich womöglich nicht so leicht durch die Namenskonvention erschließt.

In diesen Fällen lässt sich eine Methodensignatur im Repository-Interface mit einen frei vergebenen Namen mit der Annotation @Query kennzeichnen. Im Value-Attribute der Annotation formuliert man dann die Abfrage von Hand. Prinzipiell stehen dazu zwei verschiedene Abfragesprachen zur Verfügung: klassisches SQL oder JPQL. JPQL ist die Java Persistence Query Language, der Nachfolger der Hibernate Query Language.

Um etwa den Status einer Person abzufragen, sieht die Repository-Methode in JPQL wie folgt aus:

@Query("SELECT p FROM Person p WHERE p.status = 1")
List<Person> findAllActivePersons();

Natürlich lassen sich auch hier Parameter verwenden:

@Query("SELECT p FROM Person p WHERE p.firstname = ?1")
Person findPersonByFirstname(String firstname);

Die aufgeführten Eigenschaften wie der Name Person oder das Attribut status beziehen sich bei JPQL auf das Entity-Objektmodell und nicht etwa die Datenbanktabelle. Das ist insbesondere dann wichtig, wenn die Namen in der Java-Klasse und in der Datenbank nicht identisch sind.

Der gleiche Sachverhalt in SQL kann folgendermaßen formuliert werden, wobei dann hier die Datenbanktabelle Person und die Tabellenspalte status gemeint sind:

@Query(value = "SELECT * FROM Person p WHERE p.status = 1", nativeQuery = true)
List<Person> findAllActivePersons();

Auch hier ist es möglich, Parameter mit Fragezeichen und Index zu verwenden. Wichtig: Da JPQL die Standard-Abfragesprache ist, muss bei Verwendung von SQL das Attribute nativeQuery auf true gesetzt werden.

„Spring Boot“-Tutorial
Bildergalerie mit 15 Bildern

Welche der beiden Sprachen genutzt wird, ist ein bisschen Geschmackssache. JPQL ist sehr mächtig und dürfte eher den Java-Entwicklern liegen, die ja in der Objekt-Welt zu Hause sind. SQL ist weiter verbreitet und viele Abfragen liegen bereits in SQL vor.

Im nächsten Teil der Serie stellen wir Services und Datentransferobjekte in Spring-Boot-Anwendungen. Stay tuned!

(ID:47038000)

Über den Autor