Spring Boot und PostgreSQL

Bei aller Liebe zu Demo-Projekten und dem schnellen Start auf der grünen Wiese benötigen die meisten Applikationen zu irgendeinem Zeitpunkt eine Persistierung von Daten. In unserem Artikel „LIKE Queries mit Spring Boot“ haben wir bereits eine H2 Datenbank im In-Memory-Modus eingebunden. Das hat in jedem Fall auch seinen Reiz – aber die Daten sind nach jedem Herunterfahren der Applikation futsch. Für den Produktivbetrieb bieten sich daher andere Datenbanksysteme an. Sehr beliebt ist zum Beispiel PostgreSQL.
Über PostgreSQL
Das Open Source Datenbanksystem PostgreSQL wirbt auf seiner Website https://www.postgresql.org/ selbst damit, dass es seit über dreißig Jahren weiterentwickelt wird. Dadurch verfügen die aktuellen Versionen definitiv über „Serienreife“ und können in produktiven Projekten eingesetzt werden.
PostgreSQL unterstützt weitestgehend den SQL:2016-Standard. Dies macht einen optionalen Wechsel auf ein anderes Datenbank-Management-System möglich, unter der Voraussetzung, dass konsequent die Standardsyntax verwendet wird. Es gibt also keinen Grund, PostgreSQL keine Chance zu geben!
Einbindung und Konfiguration
Als Grundlage nehme ich den letzten Codestand aus dem bereits zitierten Post „LIKE Queries mit Spring Boot“.
Hier der Codestand zum Download: tasklist
Im ersten Schritt binde ich anstelle von H2 eine PostgreSQL Datenbank ein. Nichts leichter als das! Dazu benötigen wir lediglich zunächst eine Anpassung in der pom.xml.
...
<dependencies>
...
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
</dependency>
...
</dependencies>
...
Hiermit werden die benötigten Treiber geladen. Zusätzlich müssen wir Spring Boot noch mitteilen, wo unser PostgreSQL Datenbank zu finden ist. Das erfolgt in der Datei application.properties:
spring.datasource.url=jdbc:postgresql://localhost:5432/tasklistdb
spring.datasource.username=user
spring.datasource.password=pwd
Zusätzlich übergeben wir hier die URL, den Usernamen und das Passwort. Selbstverständlich würde man diese Werte auf öffentlich zugänglichen Servern noch überschreiben und vor allem ein sicheres Passwort verwenden. Aber zur lokalen Demonstration genügt das erst einmal so.
Wo ist meine Datenbank?
Wenn ich das Projekt jetzt starte, knallt es erst mal. Außer natürlich ich habe zufälligerweise lokal unter dem Port 5432 eine PostgreSQL Datenbank namens tasklistdb laufen, die zudem noch einen User namens „user“ mit dem Passwort „pwd“ hat. Zu viele Zufälle? Finde ich auch…
Wir müssen uns also eine Datenbank organisieren. Abgesehen vom Oldschool-Weg, sich eine tatsächliche PostgreSQL-Instanz lokal aufzusetzen, empfehle ich für die ersten Schritte zwei Wege: Entweder eine kostenlose Instanz bei https://www.elephantsql.com/ oder eine lokale Instanz in einem Docker-Container.
Für die erste Variante muss man sich einen Account anlegen und bekommt dann eine kostenlose Instanz gestellt. Von dieser nimmt man einfach URL, Username und Passwort und trägt diese in der Datei application.properties ein.
Alternativ besteht die Möglichkeit lokal einen Docker-Container aus einem aktuellen Image zu starten. Ich habe mir dafür ein kleines Docker-Compose File (docker-compose.yml) gebastelt:
version: '3.5'
services:
db:
image: postgres:12
restart: always
environment:
POSTGRES_USER: user
POSTGRES_PASSWORD: pwd
POSTGRES_DB: tasklistdb
ports:
- 5432:5432
Wenn auf der entsprechenden Maschine docker und docker-compose installiert sind, genügt zum Start die Ausführung des folgenden Befehls im Ordner der docker-compose.yml:
docker-compose up
Sollte es eine Fehlermeldung aufgrund der Version der Datei geben, kann diese ggf. noch heruntergesetzt werden, also in der ersten Zeile zum Beispiel auf ‘3.2‘ angepasst werden. Ist der Container gestartet, verbindet sich auch Spring Boot sofort mit der PostgreSQL Datenbank.
Exkurs: Spring Boot und Hibernate
Wir erinnern uns: Der SQL Standard bezieht sich auf relationale Datenbanksysteme, die Daten in einem festen Schema von Tabellen ablegen. Daran werden wir beim nächsten Start des Projekts erinnert, da Spring Boot uns mit dem folgenden Fehler erfreut:
Caused by: org.postgresql.util.PSQLException: ERROR: relation "task" does not exist
Im Tasklisten-Beispiel haben wir uns ja Demo-Daten beim Start des Projekts angelegt, diese wurden in der Tabelle task gespeichert. Jetzt beschwert sich PostgreSQL – irgendwie auch berechtigt – dass es diese Tabelle (bzw. Relation) nicht gibt.
Wieder haben wir unterschiedliche Möglichkeiten, dem Problem zu begegnen: Entweder legen wir die Tabelle händisch (per Konsole) an, wir machen eine Datenbankmigration mit einem Tool wie Flyway – oder wir nutzen die JPA Funktionen von Spring Boot bzw. des verwendeten Object-Relational-Mappers Hibernate. Während in Produktion in jedem Fall ein Datenbankmigrationstool zum Einsatz kommen sollte, genügt für unsere Demo folgende Konfigurationsanpassung in der application.properties:
spring.jpa.hibernate.ddl-auto=update
Was bewirkt das? Einfach gesagt: Eine Bibliothek, namens Hibernate, kümmert sich im Hintergrund darum, dass die entsprechenden Tabellen in PostgreSQL angelegt und deren Struktur angepasst wird.
Was kann Hibernate noch?
Wie schon bei der H2 Datenbank kommunizieren wir nicht direkt von Java aus mit der Datenbank. Wir nutzen einen sogenannten Object-Relational-Mapper (kurz: ORM). Java stellt dafür seit vielen Jahren mit der Java Persistence API (JPA) eine Spezifikation zur Verfügung, die von mehreren unterschiedlichen Anbietern implementiert wird. Einer der Platzhirsche in diesem Bereich ist Hibernate, was auch der Default in Spring Boot ist.
Eingebunden haben wir das ganze bereits mit der folgenden Dependency in der pom.xml:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
Einfach gesagt: Der ORM kümmert sich für uns um eine 1:1 Verbindung zwischen Datensätzen und Java-Objekten (bzw. Datenbanktabellen und Java-Klassen). Der lästige Schritt des händischen Zusammenbauens und Parsens von SQL-Queries entfällt damit komplett. Ein angenehmer Nebeneffekt ist, dass die Bibliotheken Dinge wie SQL-Injection deutlich mehr auf dem Schirm haben, als man das sonst vielleicht hätte.
Auch wenn Spring Boot Hibernate mitbringt, bzw. die einfache Einbindung ermöglicht, sind die verwendeten Konstrukte eigentlich Java Standard. Schauen wir uns nun einmal an, wie die Verbindung zwischen einer Datenbanktabelle und einer Java-Klasse genau aussieht.
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
@Entity
@Table(name = "task")
public class Task {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private long id;
@Column(name = "title")
private String title;
}
Die ersten vier Annotationen gehören zum Framework Lombok und haben für JPA erstmal keine Relevanz. Die Annotation @Entity bewirkt, dass die Klasse zu einer sogenannten JPA-Entity wird. Dies signalisiert, dass zur Java-Klasse ein Konstrukt in der PostgreSQL Datenbank gehört. Welches das ist, wird durch die Annotation @Table festgelegt. Die PostgreSQL Tabelle soll also „task“ heißen.
Spannender wird es bei den Eigenschaften der Klasse: @Column ist noch recht intuitiv, hier wird einfach die Datenbankspalte mit dem Feld in Java Klasse verbunden. Auch der Sinn der Annotation @Id ergibt sich. Hier wird der Primärschlüssel der Klasse/Tabelle festgelegt.
Da wir uns nicht selbst um die Tabellen in PostgreSQL gekümmert haben, wird die Spalte für die ID einfach als Ganzzahl angelegt. Den ersten Datensatz mit der ID 0 könnten wir noch einfügen. Danach wird es schwierig… Daher steuern wir durch die Annotation @GeneratedValue die Generierung fortlaufender IDs.
Jede Entity braucht noch ein Standardkonstrukt, also eines, ohne Parameter. Das merkt man schnell, wenn man das Projekt ohne dieses startet. JPA weist uns darauf hin.
Zurück zum Beispiel
Ok, das war jetzt doch ein etwas längerer Exkurs… Eigentlich wollten wir ja nur unsere bestehende Spring Boot Anwendung mit PostgreSQL verbinden. Wenn wir das Projekt jetzt starten, gibt es noch einen Fehler: Hibernate möchte noch wissen, welche Art von Datenbank im Hintergrund angesprochen wird. In der application.properties ergänzen wir diese Info noch kurz.
spring.jpa.database-platform=org.hibernate.dialect.PostgreSQLDialect
Damit startet unser Projekt und wir können wieder wie gehabt die URL im Browser aufrufen:
http://localhost:8080/tasks?query=clean
Jetzt werden die Tasks angezeigt, die der DemoDataProvider angelegt hat. Nach einem erneuten Start erscheinen sie dann auch doppelt. Im Gegensatz zu H2 liegt die Datenbank nun nicht mehr im Speicher und ist somit nach Beendigung der JVM weg. Also haben wir das Ziel erreicht.
Fazit
Im Rückblick muss ich sagen, dass der Titel für den Post vielleicht falsch gewählt ist. Eigentlich war das nun eher ein Crashkurs in JPA mit Hibernate innerhalb von Spring Boot. Dafür haben wir PostgreSQL als Datenbank gewählt. Annähernd analog funktioniert das für die meisten anderen relationalen Datenbanken.
Viele Spaß beim Experimentieren!

Danke für's lesen.
Julius Mischok ist Geschäftsführer der Mischok GmbH in Augsburg. Seine Kernaufgaben sind Prozessentwicklung, sowie Coaching und Schulung der Entwicklungsteams. Aktuell fokussiert sich seine Arbeit auf die Frage, wie Software schnell und mit einer maximalen Wertschöpfung produziert werden kann. Julius hat Mathematik studiert und entwickelt seit fast zwei Jahrzehnten Java. Seine Erfahrung brachte er unter anderem in Softwareprojekten für BMW, Audi, Hilti, Porsche, Allianz, Bosch, und viele mehr ein.