Modul 23 · Migration Deep Dive · Stufe 4 Daten und Transaktionen

Hibernate und JPA

proprietäres Hibernate Session API wird nachvollziehbar in Jakarta Persistence und Spring Data Repository überführt. Die Akte zeigt nicht nur das Ziel, sondern die tatsächlichen Dateien, APIs, Dependencies, Codebelege, Tests, Risiken und Cutover-Schritte.

Unit of WorkRepositoryEntity
2 → 3Produktionsdateien
58 → 45Java-Zeilen
1 → 1Testdateien
4 / 2Dependencies entfernt / neu
NIEDRIGRisiko · Score 0

Was sich konkret ändert

DimensionLegacyModernMigrationskonsequenz
ProgrammiermodellEntity, GeneratedValue, Id, TableEntity, GeneratedValue, Id, SpringBootApplication, TableAnnotationen und Containerfunktionen werden nur dort eingesetzt, wo sie eine konkrete technische Verantwortung übernehmen.
Abhängigkeiten5 direkte Dependencies3 direkte Dependencies2 neu, 4 entfernt; Versionen und transitive Auswirkungen im erfolgreichen Online-Build prüfen.
Öffentliche API5 erkannte Methoden3 erkannte MethodenMethoden werden nach fachlicher Rolle gemappt; reine Bootstrap- und Framework-Methoden sind kein fachlicher Vertrag.
Datenmodellclass HibernateBookStore, class LegacyBookclass BookEntity, class JpaModernApplication, interface BookRepositoryFeldnamen, IDs, Null-Semantik, Gleichheit und Serialisierungsform werden separat regressionstestet.
FehlerverhaltenLegacy-Exceptions und Rückgabewerteexplizitere Fach-/Framework-FehlerabbildungFehler dürfen nicht nur technisch übersetzt werden; Status, Ursache, Retrybarkeit und Client-Vertrag müssen erhalten oder versioniert werden.
TestsBestands- und Golden-Master-TestsUnit-, Slice-, Contract- und IntegrationstestsDer Modern-Pfad wird zuerst gegen denselben fachlichen Vektor geprüft und danach um neue technische Risiken ergänzt.
BetriebLegacy-Start/Lifecyclemodernes Packaging, Health und externe KonfigurationMigration mit produktionsnahen Daten, Backups, Restore-Probe und Query-/Lock-Monitoring absichern.
RollbackLegacy-Artefakt bleibt unverändertModern-Artefakt getrennt deploybarKein Rollback über Datenverlust: Schema, Nachrichten und verschlüsselte Daten müssen rückwärtslesbar oder durch Dual-Read abgesichert sein.

Fachlicher Vertrag: unverändert zu erhalten

  • Entity-Identität, Tabellenmapping und Cascade-Regeln bleiben bewusst.
  • Transaktionen werden nicht durch Open-Session-in-View versteckt.
  • Queries liefern gleiche Ergebnismengen und Sortierung.

Hauptrisiko und Testfokus

entity lifecycle, schema and query differences

entity mapping, repository query, transaction and N+1 checks

Die Modernisierung gilt erst als abgeschlossen, wenn der fachliche Vertrag automatisiert belegt ist.

Verbindliches Code- und Rollenmapping

Die Zuordnung ist semantisch: Eine Legacy-Klasse kann in mehrere moderne Rollen zerlegt werden.

Legacy-Rolle / DateiModern-Rolle / DateiBedeutung
LegacyBook.javaBookEntity.javajavax Entity wird Jakarta Entity.
HibernateBookStore.javaBookRepository.javaSession API/Unit of Work wird Spring Data Repository.
Legacy BootstrapJpaModernApplication.javaKonfiguration wechselt auf Spring Boot/JPA.

Legacy-Quellinventar

DateiTypAnnotationenöffentliche APIPatternZeilen
HibernateBookStore.javaclass HibernateBookStoreLong save(String title)
Optional<LegacyBook> find(Long id)
void close()
Unit of Work32
LegacyBook.javaclass LegacyBookEntity, GeneratedValue, Id, TableLong getId()
String getTitle()
26

Modern-Quellinventar

DateiTypAnnotationenöffentliche APIPatternZeilen
BookEntity.javaclass BookEntityEntity, GeneratedValue, Id, TableLong getId()
String getTitle()
26
BookRepository.javainterface BookRepositoryRepository8
JpaModernApplication.javaclass JpaModernApplicationSpringBootApplicationvoid main(String[] args)11

Dependency- und Laufzeitmigration

StatusDependencyVersionScopePrüfung
ENTFERNTjavax.persistence:javax.persistence-api2.2compileLegacy-Laufzeit entfällt oder wird durch Plattform/BOM ersetzt.
ENTFERNTorg.hibernate:hibernate-core5.6.15.FinalcompileLegacy-Laufzeit entfällt oder wird durch Plattform/BOM ersetzt.
ENTFERNTorg.junit.jupiter:junit-jupiterBOM/ParenttestLegacy-Laufzeit entfällt oder wird durch Plattform/BOM ersetzt.
ENTFERNTorg.slf4j:slf4j-simple2.0.17compileLegacy-Laufzeit entfällt oder wird durch Plattform/BOM ersetzt.
NEUorg.springframework.boot:spring-boot-starter-data-jpaBOM/ParentcompileNeue Laufzeit-/Testabhängigkeit; transitiv, lizenz- und security-seitig prüfen.
NEUorg.springframework.boot:spring-boot-starter-testBOM/ParenttestNeue Laufzeit-/Testabhängigkeit; transitiv, lizenz- und security-seitig prüfen.
GEÄNDERTcom.h2database:h2BOM/Parent → BOM/Parentcompile → runtimeGemeinsame Dependency; Version und Scope im effektiven POM prüfen.
Direkte POM-Daten ersetzen keinen erfolgreichen Online-Build mit transitiver SBOM- und CVE-Prüfung.

Vorher-/Nachher-Codebelege

Die folgenden Ausschnitte stammen direkt aus den enthaltenen Projekten. Dadurch ist sichtbar, welche Verantwortung tatsächlich verschoben wurde.

Legacy
LegacyBook.java
Modern
BookEntity.java

Migrationsbedeutung: javax Entity wird Jakarta Entity.

Legacy-Code

projects/23-hibernate-jpa/legacy/src/main/java/at/aydin/lab/hibernate/legacy/LegacyBook.java

package at.aydin.lab.hibernate.legacy;

import javax.persistence.*;

@Entity
@Table(name = "book")
public class LegacyBook {
    @Id
    @GeneratedValue
    private Long id;
    private String title;
    protected LegacyBook() {
    }

    public LegacyBook(String title) {
        this.title = title;
    }

    public Long getId() {
        return id;
    }

    public String getTitle() {
        return title;
    }
}

Modern-Code

projects/23-hibernate-jpa/modern/src/main/java/at/aydin/lab/hibernate/modern/BookEntity.java

package at.aydin.lab.hibernate.modern;

import jakarta.persistence.*;

@Entity
@Table(name = "book")
public class BookEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String title;
    protected BookEntity() {
    }

    public BookEntity(String title) {
        this.title = title;
    }

    public Long getId() {
        return id;
    }

    public String getTitle() {
        return title;
    }
}
Technischen Unified Diff öffnen
--- LegacyBook.java
+++ BookEntity.java
@@ -1,18 +1,18 @@
-package at.aydin.lab.hibernate.legacy;
+package at.aydin.lab.hibernate.modern;
 
-import javax.persistence.*;
+import jakarta.persistence.*;
 
 @Entity
 @Table(name = "book")
-public class LegacyBook {
+public class BookEntity {
     @Id
-    @GeneratedValue
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
     private Long id;
     private String title;
-    protected LegacyBook() {
+    protected BookEntity() {
     }
 
-    public LegacyBook(String title) {
+    public BookEntity(String title) {
         this.title = title;
     }
 
Legacy
HibernateBookStore.java
Modern
BookRepository.java

Migrationsbedeutung: Session API/Unit of Work wird Spring Data Repository.

Legacy-Code

projects/23-hibernate-jpa/legacy/src/main/java/at/aydin/lab/hibernate/legacy/HibernateBookStore.java

package at.aydin.lab.hibernate.legacy;

import org.hibernate.*;
import org.hibernate.cfg.Configuration;
import java.util.*;

// Design Pattern: Unit of Work
// Zweck: Hibernate Session und Transaction bilden eine explizite Arbeitseinheit.
public final class HibernateBookStore implements AutoCloseable {
    private final SessionFactory factory = new Configuration().addAnnotatedClass(LegacyBook.class).setProperty("hibernate.connection.url",
        "jdbc:h2:mem:hibernatelegacy;DB_CLOSE_DELAY=-1").setProperty("hibernate.connection.driver_class", "org.h2.Driver")
            .setProperty("hibernate.dialect", "org.hibernate.dialect.H2Dialect").setProperty("hibernate.hbm2ddl.auto", "create-drop")
            .buildSessionFactory();
    public Long save(String title) {
        try (Session s = factory.openSession()) {
            Transaction tx = s.beginTransaction();
            Long id = (Long) s.save(new LegacyBook(title));
            tx.commit();
            return id;
        }
    }

    public Optional<LegacyBook> find(Long id) {
        try (Session s = factory.openSession()) {
            return Optional.ofNullable(s.get(LegacyBook.class, id));
        }
    }

    public void close() {
        factory.close();
    }
}

Modern-Code

projects/23-hibernate-jpa/modern/src/main/java/at/aydin/lab/hibernate/modern/BookRepository.java

package at.aydin.lab.hibernate.modern;

import org.springframework.data.jpa.repository.JpaRepository;

// Design Pattern: Repository
// Zweck: Datenzugriff wird als deklarativer fachlicher Vertrag ausgedrückt.
public interface BookRepository extends JpaRepository<BookEntity, Long> {
}
Technischen Unified Diff öffnen
--- HibernateBookStore.java
+++ BookRepository.java
@@ -1,32 +1,8 @@
-package at.aydin.lab.hibernate.legacy;
+package at.aydin.lab.hibernate.modern;
 
-import org.hibernate.*;
-import org.hibernate.cfg.Configuration;
-import java.util.*;
+import org.springframework.data.jpa.repository.JpaRepository;
 
-// Design Pattern: Unit of Work
-// Zweck: Hibernate Session und Transaction bilden eine explizite Arbeitseinheit.
-public final class HibernateBookStore implements AutoCloseable {
-    private final SessionFactory factory = new Configuration().addAnnotatedClass(LegacyBook.class).setProperty("hibernate.connection.url",
-        "jdbc:h2:mem:hibernatelegacy;DB_CLOSE_DELAY=-1").setProperty("hibernate.connection.driver_class", "org.h2.Driver")
-            .setProperty("hibernate.dialect", "org.hibernate.dialect.H2Dialect").setProperty("hibernate.hbm2ddl.auto", "create-drop")
-            .buildSessionFactory();
-    public Long save(String title) {
-        try (Session s = factory.openSession()) {
-            Transaction tx = s.beginTransaction();
-            Long id = (Long) s.save(new LegacyBook(title));
-            tx.commit();
-            return id;
-        }
-    }
-
-    public Optional<LegacyBook> find(Long id) {
-        try (Session s = factory.openSession()) {
-            return Optional.ofNullable(s.get(LegacyBook.class, id));
-        }
-    }
-
-    public void close() {
-        factory.close();
-    }
+// Design Pattern: Repository
+// Zweck: Datenzugriff wird als deklarativer fachlicher Vertrag ausgedrückt.
+public interface BookRepository extends JpaRepository<BookEntity, Long> {
 }
Legacy
Legacy Bootstrap
Modern
JpaModernApplication.java

Migrationsbedeutung: Konfiguration wechselt auf Spring Boot/JPA.

Legacy-Code

projects/23-hibernate-jpa/legacy/src/main/java/at/aydin/lab/hibernate/legacy/LegacyBook.java

package at.aydin.lab.hibernate.legacy;

import javax.persistence.*;

@Entity
@Table(name = "book")
public class LegacyBook {
    @Id
    @GeneratedValue
    private Long id;
    private String title;
    protected LegacyBook() {
    }

    public LegacyBook(String title) {
        this.title = title;
    }

    public Long getId() {
        return id;
    }

    public String getTitle() {
        return title;
    }
}

Modern-Code

projects/23-hibernate-jpa/modern/src/main/java/at/aydin/lab/hibernate/modern/JpaModernApplication.java

package at.aydin.lab.hibernate.modern;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class JpaModernApplication {
    public static void main(String[] args) {
        SpringApplication.run(JpaModernApplication.class, args);
    }
}
Technischen Unified Diff öffnen
--- LegacyBook.java
+++ JpaModernApplication.java
@@ -1,26 +1,11 @@
-package at.aydin.lab.hibernate.legacy;
+package at.aydin.lab.hibernate.modern;
 
-import javax.persistence.*;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
 
-@Entity
-@Table(name = "book")
-public class LegacyBook {
-    @Id
-    @GeneratedValue
-    private Long id;
-    private String title;
-    protected LegacyBook() {
-    }
-
-    public LegacyBook(String title) {
-        this.title = title;
-    }
-
-    public Long getId() {
-        return id;
-    }
-
-    public String getTitle() {
-        return title;
+@SpringBootApplication
+public class JpaModernApplication {
+    public static void main(String[] args) {
+        SpringApplication.run(JpaModernApplication.class, args);
     }
 }

Umsetzungsplan mit Qualitäts-Gates

  1. Arbeitspaket 1
    LegacyBook-Mapping und Hibernate-Konfiguration dokumentieren. Nachweis: Commit/PR, automatisierter Test und aktualisierte Betriebsdokumentation.
  2. Arbeitspaket 2
    BookEntity auf jakarta.persistence übertragen und Schema validieren. Nachweis: Commit/PR, automatisierter Test und aktualisierte Betriebsdokumentation.
  3. Arbeitspaket 3
    BookRepository mit expliziten Query-Namen/Sortierungen einführen. Nachweis: Commit/PR, automatisierter Test und aktualisierte Betriebsdokumentation.
  4. Arbeitspaket 4
    Session-spezifische Unit-of-Work-Logik in Service verschieben. Nachweis: Commit/PR, automatisierter Test und aktualisierte Betriebsdokumentation.
  5. Arbeitspaket 5
    Lazy/Eager, Cascade, N+1 und Locking mit Integrationstests prüfen. Nachweis: Commit/PR, automatisierter Test und aktualisierte Betriebsdokumentation.
  6. Arbeitspaket 6
    Native Hibernate API erst nach Query- und Performancevergleich entfernen. Nachweis: Commit/PR, automatisierter Test und aktualisierte Betriebsdokumentation.

Konkreter Test- und Abnahmekatalog

IDEbenePrüfungerforderlicher Nachweis
23-HIBERNATE-JPA-A01Integration/ContractSchema-Diff enthält keine ungewollten Änderungen.Automatisierter Test und CI-Protokoll
23-HIBERNATE-JPA-A02Integration/ContractBestehende IDs und Beziehungen bleiben lesbar.Automatisierter Test und CI-Protokoll
23-HIBERNATE-JPA-A03Integration/ContractTransaktionen schließen vor API-Serialisierung sauber ab.Automatisierter Test und CI-Protokoll
23-HIBERNATE-JPA-A04Integration/ContractKeine LazyInitializationException im vorgesehenen Pfad.Automatisierter Test und CI-Protokoll
23-HIBERNATE-JPA-A05Integration/ContractQuery-Anzahl/Performance ist akzeptiert.Automatisierter Test und CI-Protokoll
23-HIBERNATE-JPA-T01UnitFachlogik ohne Container oder externen Dienst testen.Unit-Test
23-HIBERNATE-JPA-T02RegressionLegacy- und Modern-Ergebnis für denselben Golden-Master-Vektor vergleichen.Vergleichsreport
23-HIBERNATE-JPA-T03NegativeFehlerhafte, leere und grenzwertige Eingaben prüfen.Negativtest
23-HIBERNATE-JPA-T04OperationsStart, Health, Shutdown und Konfigurationsfehler prüfen.Deployment-/Startprotokoll
23-HIBERNATE-JPA-T05DatabaseRollback, Constraints, Locking und Schema-Kompatibilität mit produktionsnahen Daten prüfen.DB-Integrationstest
23-HIBERNATE-JPA-F01Fokusentity mapping, repository query, transaction and N+1 checksModulspezifischer Testreport

Risikoregister des Moduls

RisikoAuswirkungGegenmaßnahmeGate
entity lifecycle, schema and query differencesNIEDRIGentity mapping, repository query, transaction and N+1 checksvor Cutover
Neue Framework-/Library-LaufzeitabhängigkeitenMITTELSBOM, Lizenz-, CVE- und transitive Dependency-Prüfung im Online-Buildvor Release
Legacy-Verhalten war implizit an entfernte Library gekoppeltMITTELContract-/Golden-Master-Tests und gezielte Fehlerpfadevor Abschaltung

Konfiguration und Ressourcen

Keine zusätzlichen Ressourcen.

Rollback und Koexistenz

Alte und neue Persistenzadapter können auf demselben Schema lesen; paralleles Schreiben erfordert abgestimmte Cache- und Versionierungsregeln.

Abbruchkriterien

  • Fachlicher Golden-Master weicht ab.
  • Daten-, Nachrichten- oder API-Kompatibilität ist ungeklärt.
  • Fehlerquote, Latenz oder Ressourcenverbrauch überschreiten das vereinbarte Limit.
  • Monitoring oder Rückfallpfad ist nicht funktionsfähig.

Definition of Done

Direkte Arbeitslinks

⌂ Cockpit