Start / Seminare / Spring Boot 4 Grundlagen
Modul
Datenzugriff und Persistenz
4 Kapitel in diesem Modul-Video · Laufzeit
Transkript
Der gesprochene Text dieses Moduls zum Mitlesen, Überfliegen und Durchsuchen. Ein Klick auf einen Zeitstempel springt an die Stelle im Video.
Datenzugriff und Persistenz
0:00 In diesem Modul passiert etwas, das ich für den überzeugendsten Moment des ganzen Seminars halte. Wir werfen die Implementierung unserer Repositories weg — ersatzlos. Aus Klassen mit Code werden Schnittstellen ohne Code, und die Anwendung kann danach mehr als vorher. Das klingt nach einem Taschenspielertrick, ist aber ein sehr durchdachter Mechanismus.
0:21 Nebenbei lösen wir die fest verdrahteten Listen aus Modul drei ab und hängen Radwerk an zwei ganz unterschiedliche Datenbanken. Achten Sie besonders darauf, was dabei mit den Controllern passiert. Die Antwort lautet: nichts. Und genau das war der Sinn der Übung.
Datenzugriff und Persistenz
0:37 Der zweite Tag beginnt mit den Daten. Zuerst ordnen wir ein, was Spring Data eigentlich ist und wovon es uns entlastet. Dann bauen wir Radwerk auf eine relationale Datenbank um — mit Entitäten und Repositories, die sich fast von selbst schreiben. Danach die Frage, wie eine Datenbank überhaupt zu ihren Tabellen kommt und wie sie sich über Jahre weiterentwickeln lässt.
0:58 Und zum Schluss ein zweiter Speicher ganz anderer Art: MongoDB für die Anmeldungen.
Spring Data kennenlernen
1:03 Bevor wir umbauen, kurz die Einordnung. Spring Data ist kein einzelnes Werkzeug, sondern eine ganze Familie — und um zu verstehen, was sie leistet, hilft ein Blick darauf, wie mühsam Datenzugriff in Java einmal war. Spring Data ist kein Projekt, sondern eine Familie von Projekten, für relationale Datenbanken, für Dokumentspeicher, für Schlüssel-Wert-Speicher und einiges mehr.
1:26 Sie teilen sich gemeinsame Grundlagen, und deshalb fühlt sich der Umgang mit ihnen ähnlich an, egal welche Technik dahintersteht. Das ist mehr wert, als es klingt: Was Sie hier für JPA lernen, können Sie später für MongoDB fast unverändert anwenden — wir werden das in diesem Modul sogar vorführen. Der Zweck des Ganzen ist immer derselbe: den Anteil an Anbindungscode senken, damit Sie das Datenmodell beschreiben statt den Weg zur Datenbank.
1:52 Wer schon länger dabei ist, kennt diese Abfolge auswendig, und das ist genau das Problem. Verbindung öffnen, Anweisung vorbereiten, ausführen, Ergebnismenge Zeile für Zeile auslesen, und am Ende alles wieder schließen — in der richtigen Reihenfolge, auch im Fehlerfall. Das ist nicht schwierig, es ist ermüdend. Und weil es ermüdend ist und immer gleich aussieht, liest es irgendwann niemand mehr genau, was die perfekte Brutstätte für Ressourcenlecks ist.
2:18 Der vierte Punkt wiegt aber am schwersten: Die Übersetzung in Java-Objekte fehlt komplett. Bei einem Datenmodell mit Beziehungen wird das schnell zu einer eigenen kleinen Bibliothek. Diese Tabelle ist ein Stapel, kein Vergleich. Ganz unten der rohe Zugriff über Treiber und SQL. Darüber JPA, die Standardschnittstelle für die Abbildung zwischen Objekten und Tabellen — Hibernate ist die verbreitetste Umsetzung davon.
2:43 Darüber Spring Data JPA, das die Repositories beisteuert, für die Sie keine Implementierung mehr schreiben. Und ganz oben der Boot-Starter, der Datenquelle und Transaktionsverwaltung einrichtet. Wichtig ist: Keine dieser Schichten ersetzt die darunterliegende. Sie können jederzeit eine Ebene tiefer greifen, wenn Sie es brauchen. Sie müssen es nur nicht mehr für jede alltägliche Abfrage tun.
3:07 Und hier steht der Kerngedanke, um den es gleich geht: Sie definieren eine Schnittstelle, und Spring Data erzeugt daraus zur Laufzeit eine Implementierung. Es gibt also keine Klasse, die Sie im Projekt finden könnten — sie entsteht beim Start. Das ist für viele zunächst irritierend, weil man den Code nicht anschauen kann, der die eigentliche Arbeit macht.
3:27 Dazu kommt der passende Starter, der Datenquelle und Transaktionsverwaltung einrichtet, ohne dass Sie eine Konfigurationsklasse schreiben. Zusammen bedeutet das: Zwischen Ihrem Datenmodell und einer funktionierenden Datenbankanbindung liegen im Wesentlichen ein paar Annotationen. Der erste Punkt ist eine Begriffsunschärfe, die im Gespräch oft zu Missverständnissen führt: JPA ist der Standard für die Abbildung, Spring Data JPA setzt darauf auf und ergänzt die Repositories.
3:54 Wer beides gleichsetzt, sucht Erklärungen in der falschen Dokumentation. Der zweite Punkt ist eine Erwartungsfrage — die Abbildung kann viel, aber sie repariert kein schlechtes Modell. Wenn die Objektstruktur nicht zur Tabellenstruktur passt, wird es mühsam, egal welches Werkzeug Sie nehmen. Und der dritte ist wieder die alte Gewohnheit: Anbindungscode weiterzuschreiben, obwohl er längst nicht mehr nötig ist.
Mit einer relationalen Datenbank arbeiten
4:18 Jetzt bauen wir um. Zwei Dinge ändern sich: Aus unseren Records werden Entitäten, und aus unseren Repository-Klassen werden Schnittstellen. Der zweite Teil ist der, bei dem es klick machen sollte. Die Schritte sind schnell erzählt, und das ist bemerkenswert. Sie ergänzen den Starter für Spring Data JPA, dazu H2 als Datenbank und Treiber, schalten die Konsole ein, um hineinsehen zu können, und starten.
4:44 Mehr ist nicht nötig, damit eine funktionierende Datenbankverbindung entsteht. Ein Wort zu H2: Es ist klein, schnell und läuft im Speicher, was es für Demonstration und Test ideal macht — und für Produktion untauglich. Das ist keine Kritik, sondern der Zweck. Achten Sie beim Start wieder auf das Protokoll; dort steht, dass eine Datenquelle eingerichtet wurde, die Sie nirgends konfiguriert haben.
5:09 Und hier holt uns eine Entscheidung aus Modul drei ein. JPA verlangt, dass Entitätsklassen veränderlich sind — der Persistenzmechanismus muss Werte setzen können, etwa den erzeugten Schlüssel nach dem Einfügen. Records sind unveränderlich und scheiden damit aus. Das ist ein echter Zielkonflikt und kein Formfehler: Genau die Eigenschaft, die Records als Datenträger so angenehm macht, steht der Abbildung im Weg.
5:33 Also werden Filiale, Standort, Servicetermin und Leistung zu gewöhnlichen Klassen mit Feldern und Zugriffsmethoden. Merken Sie sich das, denn bei MongoDB wird das gleich wieder anders sein. Die Annotationen erzählen hier eine kleine Geschichte über die Abbildung. Die erste erklärt die Klasse zur Entität, die zweite nennt die Tabelle.
5:53 Dann kommt der Schlüssel, und die zweite Annotation daran sagt, dass die Datenbank ihn beim Einfügen selbst vergibt — ein Detail, das erklärt, warum die Klasse veränderlich sein muss. Die Spaltenangabe schließlich verbindet Feld und Datenbankspalte. Interessant wird es bei Beziehungen, wie die Fußzeile andeutet: Der Servicetermin verweist auf Filiale und Standort, und das wird über eigene Annotationen abgebildet.
6:17 Damit wird aus einer Objektbeziehung ein Fremdschlüssel — und beim Lesen wieder ein Objekt. Das ist der Moment, um den es mir geht. Das erste Repository enthält nichts. Keine Methode, kein Code, nur eine Schnittstelle, die von einer anderen erbt. Und trotzdem kann es lesen, schreiben, löschen und zählen — die Methoden stammen aus den geerbten Schnittstellen.
6:39 Das zweite geht noch einen Schritt weiter: Dort steht eine Methode, die niemand implementiert, und Spring Data leitet aus ihrem Namen ab, was sie tun soll. Der Name ist die Abfrage. Das ist ungewöhnlich, weil wir gewohnt sind, dass Namen nur benennen. Hier tragen sie Bedeutung, und das hat Konsequenzen. Die ersten beiden Punkte erklären das scheinbare Wunder.
7:02 Die Standardmethoden erbt die Schnittstelle, die eigenen leitet Spring Data ab — aus dem Namen, den Parametern und dem Rückgabetyp zusammen. Daraus folgt eine Regel für den Alltag: Methodennamen sind hier keine Geschmacksfrage mehr. Sie müssen dem Schema folgen, sonst kann Spring Data sie nicht auflösen, und die Anwendung startet nicht.
7:21 Der letzte Punkt ist der, auf den ich Sie zu Beginn hingewiesen habe: Der Controller aus Modul drei bleibt unverändert. Er ruft dieselben Methoden auf wie vorher und merkt nicht, dass dahinter jetzt eine Datenbank steht. Das Erfolgskriterium ist bewusst so formuliert: dieselben Antworten wie zuvor, aber die Daten kommen nachweislich aus H2.
7:41 Der Nachweis ist der Punkt — schauen Sie über die Konsole in die Datenbank hinein, damit Sie sicher sind, dass Sie nicht versehentlich noch die alte Liste befragen. Und achten Sie darauf, dass Sie die Controller wirklich nicht anfassen mussten. Für die Zusatzaufgabe: Erfinden Sie eine Suchmethode allein durch ihren Namen.
7:59 Wenn sie beim Start einen Fehler wirft, haben Sie den Namen falsch gebildet — das ist eine sehr lehrreiche Erfahrung, und deshalb steht sie hier. Der erste Punkt ist der Zielkonflikt von vorhin. Wenn Sie an dieser Stelle länger kämpfen: Es gibt keinen Trick, Records und JPA-Entitäten schließen sich aus. Der zweite ist der praktische Fallstrick des Ableitungsmechanismus: Ein frei erfundener Methodenname lässt die Anwendung nicht starten.
8:25 Immerhin fällt es sofort auf und nicht erst im Betrieb. Und der dritte ist eine Suche, die ins Leere läuft — man sucht die Implementierung im Projekt und findet sie nicht. Sie existiert nicht als Datei; sie entsteht beim Start. Diese Erwartung muss man einmal umstellen.
Datenbank einrichten und weiterentwickeln
8:42 Wir haben jetzt eine Datenbank, aber noch keine Tabellen und keine Daten. Wie beides dorthin kommt, ist eine Frage, deren Antwort sich unterscheidet, je nachdem ob Sie entwickeln oder betreiben. Der Ablauf hier ist ein schönes Beispiel für einen Fehler, der einem einmal passiert und den man danach nie vergisst. Man legt ein Skript mit Beispieldaten an, startet — und bekommt einen Fehler, weil die Tabellen nicht existieren.
9:07 Der Grund ist eine Reihenfolge: Das Datenskript läuft, bevor Hibernate die Tabellen anlegt. Die Lösung besteht aus zwei Teilen. Man beschreibt die Struktur in einem eigenen Skript, und man schaltet Hibernates automatische Schemaerzeugung ab. Denn sonst hätten wir zwei Instanzen, die dieselbe Aufgabe erledigen wollen, und das geht selten gut.
9:27 Die eine Einstellung auf dieser Folie ist wichtiger, als sie aussieht. Sie sagt Hibernate: Kümmere dich nicht ums Schema, das machen wir selbst. In der Entwicklung mag die automatische Erzeugung bequem sein, aber sie hat einen Haken — sie leitet die Tabellen aus Ihren Entitäten ab, und damit haben Sie keine Kontrolle über Details wie Indizes oder Feldlängen.
9:47 Sobald Sie das Schema selbst beschreiben, wird es zu einem Artefakt, über das Sie Buch führen können. Und genau dieser Gedanke führt uns zum nächsten Punkt, denn er trägt noch weiter. Skripte im Projekt sind gut für den Start, aber sie kennen nur einen Zustand: den aktuellen. Sobald eine Anwendung in Produktion läuft und sich das Schema ändert, reicht das nicht mehr — dann müssen bestehende Daten mitwandern.
10:11 Migrationswerkzeuge wie Flyway oder Liquibase lösen das über einen einfachen Kunstgriff: Sie führen in der Datenbank selbst Buch darüber, welche Änderungsschritte schon gelaufen sind. Beim Start vergleichen sie und spielen fehlende Schritte nach. Damit wird der Schemastand zu etwas Überprüfbarem statt zu etwas, das jemand im Kopf hat.
10:30 Der erste Punkt ist der entscheidende, und er ist eher eine organisatorische als eine technische Aussage. Wenn der Schemastand in der Datenbank steht, kann jeder ihn nachsehen — auch der Kollege, der das Projekt neu übernimmt, auch nachts beim Störungseinsatz. Der dritte Punkt ist der, der Fehler verhindert: Jede Umgebung durchläuft dieselbe Folge von Änderungen.
10:51 Damit ist ausgeschlossen, dass Test und Produktion auseinanderlaufen, weil jemand in einer Umgebung von Hand nachgeholfen hat. Und der vierte macht das Ganze reviewfähig — Schemaänderungen liegen im Projekt und gehen durch dieselbe Prüfung wie Code. Der erste Punkt ist der Reihenfolgefehler von vorhin, und er ist ein guter Lehrer. Der zweite ist gefährlicher: Hibernates Schemaerzeugung in Produktion aktiv zu lassen.
11:15 Im günstigen Fall passiert nichts. Im ungünstigen ändert das Framework Ihr Schema anhand einer Entität, die jemand angepasst hat — und Daten sind weg. Der dritte ist der Zustand, in dem viele Projekte leben: Schemaänderungen werden von Hand eingespielt, und der Stand steht nirgends. Das geht gut, solange die Person verfügbar ist, die es gemacht hat.
Anmeldungen in MongoDB
11:37 Zum Abschluss zeigen wir, dass das Muster über Speichertechniken hinweg trägt. Wir hängen die Anmeldungen an MongoDB — und Sie werden das meiste davon schon kennen. MongoDB ist ein dokumentenorientierter Speicher, also etwas grundlegend anderes als eine relationale Datenbank: keine Tabellen mit festen Spalten, sondern Dokumente, die ihre Struktur selbst tragen.
11:58 Trotzdem ändert sich für uns erstaunlich wenig. Der passende Starter konfiguriert die Verbindung, und weil er standardmäßig eine Datenbank auf dem eigenen Rechner erwartet, brauchen wir nicht einmal eine Einstellung. Das ist übrigens ein gutes Beispiel für die meinungsstarken Vorgaben aus Modul eins: Für die Entwicklung ist diese Annahme genau richtig, für den Betrieb setzt man den Wert dann bewusst.
12:21 Ein einzelner Befehl, und die Datenbank läuft — das ist der praktische Nutzen von Containern im Entwicklungsalltag. Keine Installation, keine Dienste, die dauerhaft im Hintergrund liegen, und nach dem Seminar ist es mit einem Befehl wieder weg. Wenn Sie Docker nicht einsetzen können, ist eine direkte Installation ebenfalls möglich; für unsere Zwecke ist der Container aber der bequemere Weg.
12:43 Behalten Sie diesen Befehl im Hinterkopf. In Modul fünf werden wir genau dieses Muster automatisieren, sodass ein Test die Datenbank selbst startet und wieder beendet. Zwei Dinge sind hier bemerkenswert. Erstens: Die Anmeldung darf ein Record bleiben. MongoDB stellt nicht dieselben Anforderungen wie JPA, deshalb entfällt der Zielkonflikt von vorhin.
13:04 Zweitens die Fußzeile, und die ist ernst gemeint — die Id-Annotation stammt hier aus einem anderen Paket als bei JPA. Beide heißen gleich, beide sehen im Code identisch aus, und wenn Ihre Entwicklungsumgebung den falschen Import vorschlägt, funktioniert es einfach nicht. Das ist ein Fehler, der Menschen schon Stunden gekostet hat, weil man ihn im Code nicht sieht. Prüfen Sie den Import.
13:29 Der zweite Punkt ist der, auf den ich das ganze Modul hingearbeitet habe: Der Controller aus Modul drei bleibt unverändert. Wir haben inzwischen zweimal die Speicherung ausgetauscht — von der Liste zu H2 und für die Anmeldungen zu MongoDB —, und die Schicht darüber hat es kein einziges Mal gemerkt. Das ist der Ertrag der Repository-Abstraktion, und er ist der Grund, warum man sie einzieht, obwohl sie anfangs wie unnötige Bürokratie wirkt.
13:54 Der letzte Punkt formuliert es als Grundsatz: Die Wahl des Speichers bleibt eine Entscheidung hinter dem Repository. Die technische Seite dieser Aufgabe ist überschaubar. Interessanter ist der Hinweis am Ende, und ich möchte, dass Sie sich dazu wirklich eine Meinung bilden: Warum sind ausgerechnet die Anmeldungen der plausiblere Kandidat für den Dokumentspeicher?
14:14 Denken Sie an die Struktur der Daten, an die Beziehungen, an die Art der Abfragen. Es gibt keine allein richtige Antwort, aber es gibt gute Begründungen — und zwei Speicher ohne Begründung zu betreiben, ist eine Entscheidung, die Sie im Betrieb doppelt bezahlen. Der Nachweis läuft über die Mongo-Shell, damit Sie es tatsächlich sehen.
14:34 Der erste Punkt ist der Import-Fehler von eben, und ich nenne ihn zum zweiten Mal, weil er so unsichtbar ist. Der zweite ist verwandt: Der Schlüsseltyp ist bei MongoDB üblicherweise ein anderer als in JPA, und wer ihn aus Gewohnheit übernimmt, bekommt beim ersten Speichern eine überraschende Fehlermeldung. Der dritte ist die Frage aus der Aufgabe, hier als Warnung formuliert. Zwei Speicher bedeuten zwei Betriebsthemen, zwei Sicherungskonzepte, zwei Fehlerquellen.
15:01 Das kann sich lohnen, aber nur, wenn Sie sagen können, warum.
Was Sie aus Modul 4 mitnehmen
15:05 Vier Sätze. Repositories sind Schnittstellen, und die Implementierung entsteht zur Laufzeit — suchen Sie sie nicht im Projekt. Methodennamen sind Abfragen und deshalb keine Geschmacksfrage mehr. Skripte reichen für den Start, aber alles, was in Produktion geht, braucht ein Migrationswerkzeug. Und der Speicher ist austauschbar, solange die Abstraktion trägt; das haben wir in diesem Modul zweimal vorgeführt.
15:29 Im nächsten Modul kommt die Frage, die spätestens jetzt naheliegt: Woher wissen wir eigentlich, dass das alles noch funktioniert? Wir schreiben Tests.
Lieber mit Trainer? Dieses Modul ist Teil unserer Team-Schulungen — mit Übungen, Ihrem eigenen Code und Fragen, die ein Video nicht beantwortet. Mehr erfahren →