Persistenzschicht und Modell

https://bildung.social/@oerinformatik/

https://oer-informatik.de/sbb03_addressmodel

tl/dr; _(ca. 6 min Lesezeit): Es werden die Abhängigkeiten für ein Model angefügt, eine Modelklasse erstellt und annotiert. Die OOP-Hintergründe für Repositories werden kurz an UML-Klassendiagrammen erläutert. Dieser Artikel ist ein Teil der Artikelserie zu einem Adressbuch-SpringBoot-Projekt. Weiter geht es dann mit dem Test des Modells über das Repository: Testen des Repositories.

Konfiguration und Abhängigkeiten

Der Ablauf beim Einfügen einer neuen Komponente ist bei Spring(Boot) weitgehend identisch:

  • Abhängigkeiten in der pom.xml (Maven) bzw. build.gradle (Gradle) eintragen

  • neue Klasse erzeugen, die mit einer @Component-Annotation annotiert wird

  • Konfigurationen anpassen - z.B. über application.properties im Ordner src/main/ressources

Um ein Model mit zugehörigem Datenspeicher (Persistenzschicht) zu erstellen müssen zwei Abhängigkeiten ergänzt werden:

  • Spring Data JPA

  • Eine Persistence Unit - hier der Einfachheit halber zunächst die in-memory-Datenbank H2

Folgende Abschnitte müssen in der pom.xml vorhanden sein:

(NB: händisch reinkopieren, IJ: kopieren oder über Code/Generate => Dependency nach “spring-boot-starter-data-jpa” und “com.h2database” suchen)

Gliederung des Projekts in Packages

Es bestehen für ein Projekt zwei grundsätzliche Ansätze, wie die einzelnen Klassen in Packages strukturiert werden:

  • Gliederung nach Begriffen der Problem- oder Fachdomäne: alle Klassen, die zu einem bestimmten Teil der Fachdomäne gehören werden in einem Package zusammengefügt. Also etwa alle Dateien, die eine Adresse verarbeiten.

  • Gliederung nach Komponenteneingenschaften: Klassen, die einen gemeinsamen Concern behandeln werden in Packages zusammengefasst: also etwa alle Controller, alle Models und alle Views.

Für beide Varianten gibt es Vor- und Nachteile. Wir erstellen hier eine Struktur basierend auf der Fachdomäne: alle Adress-Klassen werden in einem Package zusammengefasst.

Hierzu muss zunächst ein neues Package erstellt werden, also zunächst ein neuer Ordner unterhalb von addressbook (Rechtsklick) mit Namen address:

Neuen Ordner (address) unterhalb von addressbook anlegen
Neuen Ordner (address) unterhalb von addressbook anlegen
Neue Javadatei(Address.java) in diesen Ordner speichern
Neue Javadatei(Address.java) in diesen Ordner speichern

Die Modell-Klasse

Die neu angelegte Klasse hat bereits einen Rumpf:

Dieses Modell wird als POJO umgesetzt, das zusätzlich mit Annotationen versehen wird. Als POJO (Plain Old Java Object) im engeren Sinne werden einfache Java-Klassen verstanden, die nur von Object erben, keine Interfaces implementieren und nicht annotiert sind. Häufig verfügen POJOs nur über Attribute, Getter und Setter.

Ein Model wird mit der Annotation @Entity versehen - und damit hat JPA im Prinzip schon alle Information, die es benötigt.

Damit die Annotation @Entity bekannt ist muss nur noch die Persistenzumgebung importiert werden. Schließlich liegt die Annotation im Paket javax.persistence:

Trägt die Tabelle in der Datenbank einen anderen Namen als die Klasse (z.B. das deutsche Adressen statt englisch address), so kann dieser mit der Annotation @Table(name = "Adressen") festgelegt werden.

Es folgt die Deklaration der Attribute:

Auch die Felder können annotiert werden, i.d.R. ist dies aber nur beim Primärschlüssel (id) erforderlich. Die Annotation @Column bietet einige Parameter, die interessant sein könnten:

  • String name: Der Name des Attributs in der Tabelle (Spaltenname) weicht vom Klassenattributnamen ab

  • boolean unique: bei unique=true muss der Wert einzigartig sein

  • boolean nullable: bei nullable=false darf der Attributwert nicht NULL sein

  • boolean updatable / insertable: Attribut wird in UPDATE / INSERT-Befehlen ignoriert, wenn diese flag expizit auf false gestellt wird (updatable=false)

  • String table: Wenn dieses Attribut in einer anderen Tabelle als die Primäre Tabelle gespeichert wird, kann der Name hier angegeben werden.

  • int length: Wenn die Zeichenlänge eines Strings von max. 255 abweicht, dann dies hiermit angegeben werden.

  • int precision / scale: Gibt die Anzahl der Nachkommastellen an, falls der Datentyp Decimal genutzt wird (muss analog zur Angabe per DDL gesetzt werden)

Eine sinnvolle Annotation eines Attributs könnte also sein:

Darüber hinaus müssen Konstruktoren eingefügt werden: ein Default-Konstruktor (weil die JPA es verlangt, dieser wird jedoch nicht genutzt) und ein Konstruktor, der alle Attribute setzt, die nicht mit @GeneratedValue annotiert sind:

VSCode bietet uns im Kontextmenü (rechte Maustaste im Editorfenster beim Klick auf einen Attributnamen) unten den Punkt “Quellaktion” an, der einiges vereinfachen wird:

Kontextmenü Quellaktion
Kontextmenü Quellaktion

Damit lassen sich zunächst alle Getter und Setter, später auch der Konstruktor setzen.

Getter und Setter automatisch setzen
Getter und Setter automatisch setzen

Im Fall des Konstruktors sollten alle Attribute bis auf id ausgewählt werden.

Den Default-Konstruktor ohne Parameter müssen wir manuell eingeben. Die beiden Konstruktoren wären also etwa:

Wir ergänzen noch einen abgekürzten Konstruktor, damit wir auch Objekte nur mit Vorname und Nachname bilden können:

Es gehört zum guten Stil, dass man die toString()-Methode ebenso überschreibt und alle relevanten Attribute ausgibt (Auch hier hilft die Codegenerierung von oben):

Erstellen eines neuen Repositories

Eigentlich weiß Springboot jetzt alles, was es wissen muss, um Instanzen von Address zu erzeugen und deren Attribute in einer Datenbank zu speichern. Es fehlen nur noch Methoden, die die Create, Read, Update und Delete- Aktionen (CRUD) für die Datenbank implementieren. Eine Klasse, die genau das tut, nennt sich ein Repository.

Repositories sind Komponenten, die bestimmte fachliche Klassen verwalten. Um die Implementierung müssen wir uns aber nicht mehr kümmern, das übernimmt das Framework. Wir legen lediglich ein Interface an, SpringBoot implementiert die fehlenden Methoden an Hand des übergebenen Models zur Laufzeit automatisch.

Im address-Ordner legen wir eine neue Datei AddressRepository.java an. In der Datei definieren wir ein Interface, legen fest, dass es vom CrudRepoistory erbt (eine Vorlage von Spring), sowie, dass die generische Typparameter das Address und der id-Typen Long sind:

Der Rest passiert per Zauberhand: Das Spring-Framework implementiert (vereinfacht gesagt) zur Laufzeit eine konkrete Klasse, die unsere Address-Klasse nutzt:

Es mag erstaunen, dass diese Repository-Interfaces nie direkt implementiert werden müssen: Spring sucht zunächst Implementierungen, versucht dann über Spring Data Abfragen automatisch zu generieren oder zuletzt die Methoden der Referenzimplementierung SimpleJpaRepository zu nutzen.

Vertiefung der Annotationen für das Modell

Erweiterung der Annotationen für Attribute

Es gibt eine ganze Reihe weiterer Annotationen, die verwendet werden können. Zum Teil sind diese an eine spezielle Implementierung der JPA (i.d.R. Hibernate) gebunden und nicht generell gültig.

  • @GeneratedValue(strategy = GenerationType.AUTO): hier können auch noch die Strategien IDENTITY, SEQUENCE, und TABLE ausgewählt werden.

  • @NotNull: kann auf Methoden, Attribute oder Parameter angewendet werden (bei Attributen kann auch @Column(nullable=false) gesetzt werden)

  • @NaturalId: Sofern das Domänenmodell über einen natürlichen Schlüssel verfügt kann dieser hiermit annotiert werden.

  • @NotEmpty: für Zeichenketten - verhindert Leerstrings.

  • @Size(min = 10, max = 32): gibt an, wie lange eine Zeichenkette minimal / maximal sein darf

Nächste Schritte

Dieser Artikel ist ein Teil der Artikelserie zu einem Adressbuch-SpringBoot-Projekt.

Als nächstes sollte das Modell und das Repository einmal benutzt werden, um die Funktionalität zu verstehen und zu prüfen, ob alles so klappt, wie es soll. Dazu muss

  • Eine SpringBoot-Testumgebung geschaffen

  • für die wesentlichen Methoden (CRUD) jeweils ein Test geschrieben werden

Weiter geht es also mit dem Test des Modells über das Repository: Testen des Repositories.


Hinweis zur Nachnutzung als Open Educational Resource (OER)

Dieser Artikel und seine Texte, Bilder, Grafiken, Code und sonstiger Inhalt sind - sofern nicht anders angegeben - lizenziert unter CC BY-SA 4.0. Nennung gemäß TULLU-Regel bitte wie folgt: Persistenzschicht und Modell” von Hannes Stein, Lizenz: CC BY-SA 4.0. Der Artikel wurde unter https://oer-informatik.de/sbb03_addressmodel veröffentlicht, die Quelltexte sind in weiterverarbeitbarer Form verfügbar im Repository unter https://gitlab.com/oer-informatik/java-springboot/Backend. Stand: 04.09.2023.

[Kommentare zum Artikel lesen, schreiben] / [Artikel teilen] / [gitlab-Issue zum Artikel schreiben]

Kommentare gerne per Mastodon, Verbesserungsvorschläge per gitlab issue (siehe oben). Beitrag teilen: