# Data Dependencies

In build.gradle.kts muss im plugins {} Block nebst plugin.spring noch plugin.jpa hinzugefügt werden:

plugins {
  // ...
  kotlin("plugin.jpa") version "1.4.30"
}

Unter dependencies {} muss die folgende Zeile hinzugefügt werden:

implementation("org.springframework.boot:spring-boot-starter-data-jpa")

Beides wird von Spring Initializr automatisch gemacht, falls dort als Dependency Spring Data JPA ausgewählt wurde.

# Datenbanken

Spring Data erlaubt den Zugriff auf verschiedene relationale Datenbanken (MySQL, PostgreSQL, ...) sowie NoSQL Datenbanken (MongoDB, Apache Cassandra) und auch Redis.

Nachfolgend sind die Gradle-Dependencies für die gängigsten, relationalen Datenbanken und beispielhafte Connection-Strings gelistet.

Falls für den Zugriff Benutzername und Passwort benötigt werden, können diese in der Konfiguration gesetzt werden:

spring.datasource.username=
spring.datasource.password=

WARNING

Bestimmte Tabellen- oder Feldnamen können Probleme verursachen, falls diese reservierte Wörter wie z. B. "group" sind. Um dies zu vermeiden, sollte die folgende Konfiguration gesetzt werden.

spring.jpa.properties.hibernate.globally_quoted_identifiers=true

Spring benutzt im Hintergrund den Connection-Pool Hikari, hat also ggf. mehrere Verbindungen zur Datenbank gleichzeitig offen. Die maximale Anzahl kann konfiguriert werden:

spring.datasource.hikari.maximum-pool-size=3

Es sollte am besten auch die maximale Zeit konfiguriert werden, die eine einzelne Connection offen bleiben darf (in Millisekunden), bis Hikari sie schließt und eine frische Connection aufmacht (falls z. B. ein MariaDB-Server den Standardwert von 10 Minuten für wait_timeout eingestellt hat, sollte man die Connection etwas vorher schließen):

# 9 minutes
spring.datasource.hikari.max-lifetime=540000

Da die Datenbanktreiber nur zur Laufzeit und nicht zur Entwicklung benötigt werden, sind sie mit runtimeOnly gekennzeichnet. Dadurch tauchen deren Klassen nicht unnötig beim Codeschreiben auf.

# H2

Ist diese Dependency im Classpath vorhanden, benutzt Spring automatisch eine Embedded H2 Datenbank. Es muss also kein Connection-String konfiguriert werden.

WARNING

Es wird nur eine In-Memory Datenbank erzeugt. Nach Anwendungsende sind die Daten weg!

Diese Datenbank sollte nur für Entwicklungszwecke / Tests benutzt werden!

Dependency:

runtimeOnly("com.h2database:h2")

Falls anstatt einer H2 In-Memory Datenbank eine H2 Datendatei auf die Festplatte geschrieben werden soll, kann der Connection-String entsprechend angegeben werden. Hier wird im aktuellen Verzeichnis eine "test-db" Datendatei abgelegt.

Connection-String:

spring.datasource.url=jdbc:h2:./test-db

Die folgenden Einträge sollten dann der .gitignore Datei hinzugefügt werden, damit die Datenbanken nicht im Git landen:

*.mv.db
*.trace.db

# MariaDB

Dependency:

runtimeOnly("org.mariadb.jdbc:mariadb-java-client")

Connection-String:

spring.datasource.url=jdbc:mariadb://some.host:3306/db-name

WARNING

Es sollte nicht zwischen MySQL und MariaDB Treibern für eine Connection zur selben Datenbank gewechselt werden. Je nach Treiber legt Hibernate nämlich Felder, Constraints, Hilfstabellen, etc. möglicherweise mit leichten Unterschieden an.

# MySQL

Dependency:

runtimeOnly("mysql:mysql-connector-java")

Connection-String:

spring.datasource.url=jdbc:mysql://some.host:3306/db-name

WARNING

In manchen Fällen erkennt der MySQL-Treiber die Zeitzone der Datenbank nicht (z. B. wenn mit MySQL Treiber auf eine MariaDB Datenbank zugegriffen wird, was ja aber vermieden werden sollte). Man muss diese dann im Connection-String Parameter serverTimezone angeben:

spring.datasource.url=jdbc:mysql://some.host:3306/db-name?serverTimezone=Europe/Berlin

# MSSQL

Dependency:

runtimeOnly("com.microsoft.sqlserver:mssql-jdbc")

Connection-String:

spring.datasource.url=jdbc:sqlserver://some.host:1433;database=db-name

# PostgreSQL

Dependency:

runtimeOnly("org.postgresql:postgresql")

Connection-String:

spring.datasource.url=jdbc:postgresql://some.host:5432/db-name

# SQLite

WARNING

Wird noch nicht wirklich unterstützt, hier nur zur Vollständigkeit.

Dependency:

runtimeOnly("org.xerial:sqlite-jdbc")
runtimeOnly("com.github.gwenn:sqlite-dialect:0.1.2")

Connection-String:

spring.datasource.url=jdbc:sqlite:test.db
spring.datasource.hikari.maximum-pool-size=1

# Logging

Der Verbindungsaufbau sowie Details zum Connection-Pool können im Log mit folgender Konfiguration beobachtet werden:

logging.level.com.zaxxer.hikari=DEBUG

Um zu sehen, welche SQL-Statements genau ausgeführt werden (nur für Debugging, geht auf die Performance):

spring.jpa.show-sql=true

Diese SQL Abfragen werden ohne Zeilenumbrüche ausgegeben. Um sie formatiert über mehrere Zeilen auszugeben:

spring.jpa.properties.hibernate.format_sql=true

Die geloggten Prepared Statements enthalten Platzhalter in Form von Fragezeichen. Möchte man die eingesetzten Werte dafür (sowie die bei SELECT selektierten Werte) sehen:

logging.level.org.hibernate.type.descriptor.sql=TRACE

Hibernate kann zu Datenbank-Sessions Statistiken ins Log schreiben (Timings zu Statements, Cache hits etc.). Da dies auf die Performance geht, sollte es nur zur Entwicklen oder Debuggen benutzt werden.

spring.jpa.properties.hibernate.generate_statistics=true

# Caching

Hibernate kann Entities zwischenspeichern, damit diese nicht bei jeder Anfrage neu aus der Datenbank geholt werden müssen. Das funktioniert natürlich nur, wenn Hibernate die Datenhoheit hat und so weiß, wann sich Entities ändern. Es unterstützt verschiedene Cache-Provider. Möchte man beispielsweise Ehcache benutzen, sind folgende Dependencies in build.gradle.kts nötig:

implementation("org.hibernate:hibernate-jcache")
runtimeOnly("org.ehcache:ehcache")

Dann muss der Cache noch angebunden werden. Hier ist eine Beispielkonfiguration (man kann in extra Dateien die Caches genau konfigurieren):

spring.jpa.properties.javax.persistence.sharedCache.mode=ALL
spring.jpa.properties.hibernate.cache.use_query_cache=true
spring.jpa.properties.hibernate.cache.use_second_level_cache=true
spring.jpa.properties.hibernate.cache.region.factory_class=org.hibernate.cache.jcache.JCacheRegionFactory
spring.jpa.properties.hibernate.javax.cache.missing_cache_strategy=create

# Hilfe

Eine gute Sammlung von Themen findet sich hier: en.wikibooks.org/wiki/Java_Persistence (opens new window)