Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

In db-1 hast du gelernt, wie Tabellen, Primärschlüssel, Fremdschlüssel, Constraints und SQL-Abfragen funktionieren. Jetzt beginnt der zweite Block von DB-2. Die drei Kapitel dieses Blocks hängen eng zusammen: Zuerst klären wir, warum ein ORM in der Entwicklung eingesetzt wird und warum JPA/Hibernate im Java-Umfeld wichtig ist. Danach zeigt Flyway, wie das passende Schema kontrolliert entsteht. Zum Schluss lesen wir, wie das Starterprojekt db-2-app als Spring-Boot-Anwendung auf dieses Schema zugreift.

In diesem ersten Teil übersetzt du dein Datenbankwissen in ein Spring-Boot-Projekt: Du richtest JPA/Hibernate ein, verbindest es mit PostgreSQL und erkennst, welche Teile des Codes für das Mapping zwischen Java-Objekten und relationalen Tabellen zuständig sind.

Wir arbeiten mit dem Starterprojekt db-2-app. Das Projekt ist absichtlich klein: Es zeigt genau die Bausteine, die du brauchst, um JPA/Hibernate in einem Spring-Boot-Projekt einzurichten und zu prüfen. Hibernate ist dabei der konkrete ORM-Provider, Spring Data JPA liefert Repository-Abstraktionen und JPA beschreibt die standardisierten Mapping-Konzepte Hibernate Team (2026) VMware, Inc. (2026).

Warum ORM?

In einer relationalen Datenbank denkst du in Tabellen, Zeilen, Spalten und Beziehungen. In Java arbeitest du dagegen mit Klassen, Objekten, Methoden und Typen. JPA/Hibernate verbindet diese beiden Welten.

Ein ORM, also Object-Relational Mapping, hilft genau dort, wo diese beiden Modelle im Alltag ständig übersetzt werden müssen:

Das bedeutet nicht, dass SQL verschwindet. Es bedeutet: Für einfache CRUD-Operationen und viele Standardabfragen schreibt das Framework viel technische Arbeit. Du musst trotzdem verstehen, welche Tabelle gelesen wird, welche Spalte zu welchem Feld gehört und welche Regeln PostgreSQL selbst schützen muss. Ein ORM ersetzt kein Datenbankverständnis; es macht Datenbankzugriff im Anwendungscode strukturierter.

Warum JPA mit Hibernate?

JPA ist im Java-Umfeld der Standard für ORM-Konzepte wie Entity, Primärschlüssel, Spaltenmapping und Repository-Zugriff. Hibernate ist ein konkreter JPA-Provider, der diese Konzepte ausführt. Spring Data JPA baut darauf auf und integriert Repositories in Spring Boot.

Diese Trennung ist nützlich: Der Code orientiert sich an den bekannten JPA-Konzepten, während Hibernate die konkrete Laufzeitumsetzung übernimmt. In Spring Boot ist diese Kombination verbreitet, weil sie Datenzugriff, Transaktionen, Konfiguration und Tests gut mit dem restlichen Framework verbindet.

Die zentrale Frage in diesem Teil von Block 2 lautet:

Was muss in einem Spring-Boot-Projekt vorhanden sein, damit JPA/Hibernate sauber mit PostgreSQL arbeitet?

Projektstellen im Überblick

Im Starterprojekt findest du die relevanten Teile an wenigen Stellen:

Datei im db-2-appRolle
pom.xmlaktiviert Spring Data JPA, Hibernate, PostgreSQL-Treiber und Flyway
src/main/resources/application.ymlkonfiguriert Datenbankverbindung, Flyway und JPA/Hibernate
src/main/resources/db/migration/V1__starter_ticket_schema.sqlerstellt das Datenbankschema für das Starterprojekt
TicketEntity.javabeschreibt die Tabelle app_starter.tickets als Java-Klasse
TicketRepository.javastellt CRUD und Query Methods für Tickets bereit
TicketService.javanutzt Repository und Transaktion für den fachlichen Ablauf
TicketMapper.javatrennt Entity und API-Modell

Diese Reihenfolge ist kein Zufall: Erst braucht das Projekt die richtigen Abhängigkeiten, dann eine Datenbankverbindung, danach ein reales Schema, dann ein Mapping und schliesslich Code, der dieses Mapping nutzt.

Abhängigkeiten in pom.xml

JPA/Hibernate wird im Starterprojekt nicht manuell zusammengesucht. Spring Boot bringt die passende Integrationsschicht über Starter-Abhängigkeiten mit.

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

Diese Abhängigkeit aktiviert Spring Data JPA und bindet Hibernate als ORM-Provider in die Spring-Boot-Anwendung ein. Damit kann Spring später Repositories erkennen, Entities verwalten und Datenbankoperationen über JPA ausführen.

Für PostgreSQL braucht die Anwendung zusätzlich den JDBC-Treiber:

<dependency>
    <groupId>org.postgresql</groupId>
    <artifactId>postgresql</artifactId>
    <scope>runtime</scope>
</dependency>

Der Treiber ist die technische Verbindung zwischen Java und PostgreSQL. Ohne ihn weiss die Anwendung nicht, wie sie mit einer PostgreSQL-Datenbank sprechen soll.

Flyway ist separat eingebunden:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-flyway</artifactId>
</dependency>
<dependency>
    <groupId>org.flywaydb</groupId>
    <artifactId>flyway-database-postgresql</artifactId>
</dependency>

Damit ist die Verantwortung klar verteilt: Flyway erstellt und ändert das Schema, Hibernate nutzt und validiert es.

Konfiguration in application.yml

Die wichtigste Setup-Datei für diesen Block ist src/main/resources/application.yml. Sie verbindet Spring Boot, PostgreSQL, Flyway und Hibernate.

spring:
  datasource:
    url: jdbc:postgresql://localhost:5433/ticket_system
    username: ticket_user
    password: ticket_user
  flyway:
    default-schema: app_starter
    schemas: app_starter
  jpa:
    hibernate:
      ddl-auto: validate
    properties:
      hibernate:
        default_schema: app_starter
    open-in-view: false

spring.datasource.* beschreibt die Datenbankverbindung. Die Anwendung verbindet sich mit der DB-2-Kursdatenbank ticket_system auf Port 5433.

spring.flyway.* sagt Flyway, in welchem Schema die Migrationen laufen. Im Starterprojekt ist das app_starter. Das vollständige Referenzschema des Ticket-Systems bleibt dadurch getrennt vom kleinen Lernschema der App.

spring.jpa.hibernate.ddl-auto: validate ist eine bewusste Kursentscheidung. Hibernate soll die Tabellen nicht automatisch erstellen oder verändern. Es prüft beim Start nur, ob Entity und Datenbankschema zusammenpassen. Wenn die Tabelle fehlt oder eine Spalte nicht passt, soll die Anwendung sichtbar scheitern.

hibernate.default_schema: app_starter sorgt dafür, dass Hibernate das richtige PostgreSQL-Schema verwendet. open-in-view: false verhindert, dass JPA-Sessions bis in die Web-Antwort hinein offen bleiben. Das macht Datenzugriff im Service-Layer sichtbarer und bereitet auf spätere Performance- und Transaktionsthemen vor.

Flyway erstellt, Hibernate validiert

Im Starterprojekt liegt das Schema in:

src/main/resources/db/migration/V1__starter_ticket_schema.sql

Die Migration erstellt die Tabelle:

CREATE TABLE app_starter.tickets (
    id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
    title TEXT,
    status TEXT,
    created_at TIMESTAMPTZ DEFAULT now()
);

Für den Unterricht ist diese Tabelle absichtlich noch zu schwach modelliert. Genau darum geht es später in der Datenbanklogik-Aufgabe. Für JPA/Hibernate ist hier zuerst wichtig: Das Schema kommt aus SQL, nicht aus Java.

Das ist eine professionelle Grundhaltung: Datenbankschemaänderungen gehören in versionierte Migrationen. Hibernate darf helfen, aber es ersetzt in diesem Kurs nicht die bewusste Schemaarbeit.

Entity Mapping mit TicketEntity

Die Entity ist die Java-Sicht auf eine Tabelle. Im Starterprojekt liegt sie in TicketEntity.java.

@Entity
@Table(name = "tickets", schema = "app_starter")
@Data
@NoArgsConstructor(access = AccessLevel.PROTECTED)
class TicketEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(name = "title")
    private String title;

    @Column(name = "status")
    private String status;

    @Column(name = "created_at", insertable = false, updatable = false)
    private OffsetDateTime createdAt;
}

@Entity macht die Klasse für JPA verwaltbar. @Table verbindet sie mit app_starter.tickets. Damit ist klar: Objekte dieser Klasse stehen für Zeilen in dieser Tabelle.

@Id markiert den Primärschlüssel. Aus db-1 kennst du die Regel: Ein Primärschlüssel identifiziert einen Datensatz eindeutig. @GeneratedValue(strategy = GenerationType.IDENTITY) passt zur PostgreSQL-Spalte BIGINT GENERATED ALWAYS AS IDENTITY.

@Column(name = "title") und @Column(name = "status") verbinden Java-Felder mit Tabellenspalten. created_at ist anders: Die Datenbank setzt den Wert mit DEFAULT now(). Darum steht im Mapping insertable = false, updatable = false. Der Anwendungscode soll diesen Wert lesen, aber nicht selbst schreiben.

Repository mit Spring Data JPA

Das Repository ist die Schnittstelle zum Datenzugriff:

interface TicketRepository extends JpaRepository<TicketEntity, Long> {

    List<TicketEntity> findByStatusOrderByCreatedAtDesc(String status);
}

JpaRepository<TicketEntity, Long> sagt Spring Data JPA zwei Dinge:

Dadurch erhält das Repository Standardoperationen wie findAll(), findById(...), save(...) und delete(...). Die Methode findByStatusOrderByCreatedAtDesc ist eine abgeleitete Query Method. Spring Data JPA liest den Methodennamen und erzeugt daraus eine Abfrage nach status, sortiert nach created_at absteigend VMware, Inc. (2026).

Das ist bequem, aber nicht beliebig skalierbar. Für einfache, gut lesbare Fälle ist eine Query Method sinnvoll. Sobald der Name lang, mehrdeutig oder fachlich schwer prüfbar wird, sind JPQL oder native SQL in späteren Blöcken besser geeignet.

Service, Mapper und DTO

JPA-Entities sind Datenbankmodelle. Sie sollten nicht direkt als API-Antworten nach aussen gegeben werden. Im Starterprojekt trennen drei Bausteine diese Verantwortung:

@Transactional(readOnly = true)
List<TicketResponse> findTickets(String status) {
    List<TicketEntity> tickets = StringUtils.hasText(status)
            ? ticketRepository.findByStatusOrderByCreatedAtDesc(status)
            : ticketRepository.findAll();

    return tickets.stream()
            .map(ticketMapper::toResponse)
            .toList();
}

Der Service entscheidet, welche Repository-Methode genutzt wird. @Transactional(readOnly = true) macht sichtbar, dass dies ein lesender Datenbankvorgang ist.

Der Mapper übersetzt zwischen API-Modell und Entity:

@Mapper(componentModel = "spring")
interface TicketMapper {

    TicketResponse toResponse(TicketEntity entity);

    @Mapping(target = "id", ignore = true)
    @Mapping(target = "createdAt", ignore = true)
    TicketEntity toEntity(CreateTicketRequest request);
}

Beim Erstellen eines Tickets ignoriert der Mapper id und createdAt, weil diese Werte von PostgreSQL kommen. Das ist ein gutes Beispiel für die Zusammenarbeit der Schichten: Die API liefert fachliche Eingaben, die Entity beschreibt die Datenbankzeile, PostgreSQL erzeugt technische Werte, und der Mapper hält die Modelle auseinander.

Setup-Checkliste

Wenn du JPA/Hibernate in einem Spring-Boot-Projekt einrichtest, kannst du diese Reihenfolge verwenden:

  1. spring-boot-starter-data-jpa in pom.xml einbinden.

  2. PostgreSQL-Treiber als Runtime-Abhängigkeit ergänzen.

  3. Flyway einbinden, wenn das Schema versioniert entstehen soll.

  4. spring.datasource.url, username und password in application.yml setzen.

  5. Flyway-Schema konfigurieren, zum Beispiel app_starter.

  6. spring.jpa.hibernate.ddl-auto bewusst setzen. Für Kurs- und Teamarbeit: validate.

  7. hibernate.default_schema auf das erwartete PostgreSQL-Schema setzen.

  8. Eine Flyway-Migration für die Tabelle schreiben.

  9. Eine Entity mit @Entity, @Table, @Id und @Column erstellen.

  10. Ein JpaRepository<Entity, IdTyp> anlegen.

  11. Datenzugriff über Service und Transaktion kapseln.

  12. Entity und API-Modell mit einem Mapper trennen.

  13. Anwendung starten oder Tests ausführen und Mappingfehler ernst nehmen.

Für das Starterprojekt genügt als schneller Check:

cd db-2-app
./mvnw test

Wenn PostgreSQL lokal laufen soll:

cd db-2/postgres
podman compose up -d

Danach kannst du die Anwendung im App-Projekt starten:

./mvnw spring-boot:run

Typische Stolperstellen

Unterrichtsstruktur

  1. Von DB-1 zu ORM: Warum Java-Objekte und relationale Tabellen übersetzt werden müssen.

  2. JPA/Hibernate im Spring-Boot-Projekt einrichten: pom.xml, application.yml, Flyway und ddl-auto.

  3. Guided Setup Review am db-2-app: Entity, Repository, Service, Mapper und DTO-Trennung prüfen.

Der nächste Teil von Block 2 vertieft Flyway: Du schaust genauer darauf, wie Migrationen versioniert werden, warum bereits ausgeführte SQL-Dateien unveränderlich bleiben und wie Schemaänderungen mit bestehenden Daten sicher geplant werden.

References
  1. Hibernate Team. (2026). Hibernate ORM User Guide. https://docs.jboss.org/hibernate/orm/current/userguide/html_single/Hibernate_User_Guide.html
  2. VMware, Inc. (2026). Spring Data JPA Reference Documentation. https://docs.spring.io/spring-data/jpa/reference/