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.
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.
| Dimension | Legacy | Modern | Migrationskonsequenz |
|---|---|---|---|
| Programmiermodell | Entity, GeneratedValue, Id, Table | Entity, GeneratedValue, Id, SpringBootApplication, Table | Annotationen und Containerfunktionen werden nur dort eingesetzt, wo sie eine konkrete technische Verantwortung übernehmen. |
| Abhängigkeiten | 5 direkte Dependencies | 3 direkte Dependencies | 2 neu, 4 entfernt; Versionen und transitive Auswirkungen im erfolgreichen Online-Build prüfen. |
| Öffentliche API | 5 erkannte Methoden | 3 erkannte Methoden | Methoden werden nach fachlicher Rolle gemappt; reine Bootstrap- und Framework-Methoden sind kein fachlicher Vertrag. |
| Datenmodell | class HibernateBookStore, class LegacyBook | class BookEntity, class JpaModernApplication, interface BookRepository | Feldnamen, IDs, Null-Semantik, Gleichheit und Serialisierungsform werden separat regressionstestet. |
| Fehlerverhalten | Legacy-Exceptions und Rückgabewerte | explizitere Fach-/Framework-Fehlerabbildung | Fehler dürfen nicht nur technisch übersetzt werden; Status, Ursache, Retrybarkeit und Client-Vertrag müssen erhalten oder versioniert werden. |
| Tests | Bestands- und Golden-Master-Tests | Unit-, Slice-, Contract- und Integrationstests | Der Modern-Pfad wird zuerst gegen denselben fachlichen Vektor geprüft und danach um neue technische Risiken ergänzt. |
| Betrieb | Legacy-Start/Lifecycle | modernes Packaging, Health und externe Konfiguration | Migration mit produktionsnahen Daten, Backups, Restore-Probe und Query-/Lock-Monitoring absichern. |
| Rollback | Legacy-Artefakt bleibt unverändert | Modern-Artefakt getrennt deploybar | Kein Rollback über Datenverlust: Schema, Nachrichten und verschlüsselte Daten müssen rückwärtslesbar oder durch Dual-Read abgesichert sein. |
entity lifecycle, schema and query differences
entity mapping, repository query, transaction and N+1 checks
Die Zuordnung ist semantisch: Eine Legacy-Klasse kann in mehrere moderne Rollen zerlegt werden.
| Legacy-Rolle / Datei | Modern-Rolle / Datei | Bedeutung |
|---|---|---|
| LegacyBook.java | BookEntity.java | javax Entity wird Jakarta Entity. |
| HibernateBookStore.java | BookRepository.java | Session API/Unit of Work wird Spring Data Repository. |
| Legacy Bootstrap | JpaModernApplication.java | Konfiguration wechselt auf Spring Boot/JPA. |
| Datei | Typ | Annotationen | öffentliche API | Pattern | Zeilen |
|---|---|---|---|---|---|
| HibernateBookStore.java | class HibernateBookStore | — | Long save(String title) Optional<LegacyBook> find(Long id) void close() | Unit of Work | 32 |
| LegacyBook.java | class LegacyBook | Entity, GeneratedValue, Id, Table | Long getId() String getTitle() | — | 26 |
| Datei | Typ | Annotationen | öffentliche API | Pattern | Zeilen |
|---|---|---|---|---|---|
| BookEntity.java | class BookEntity | Entity, GeneratedValue, Id, Table | Long getId() String getTitle() | — | 26 |
| BookRepository.java | interface BookRepository | — | — | Repository | 8 |
| JpaModernApplication.java | class JpaModernApplication | SpringBootApplication | void main(String[] args) | — | 11 |
| Status | Dependency | Version | Scope | Prüfung |
|---|---|---|---|---|
| ENTFERNT | javax.persistence:javax.persistence-api | 2.2 | compile | Legacy-Laufzeit entfällt oder wird durch Plattform/BOM ersetzt. |
| ENTFERNT | org.hibernate:hibernate-core | 5.6.15.Final | compile | Legacy-Laufzeit entfällt oder wird durch Plattform/BOM ersetzt. |
| ENTFERNT | org.junit.jupiter:junit-jupiter | BOM/Parent | test | Legacy-Laufzeit entfällt oder wird durch Plattform/BOM ersetzt. |
| ENTFERNT | org.slf4j:slf4j-simple | 2.0.17 | compile | Legacy-Laufzeit entfällt oder wird durch Plattform/BOM ersetzt. |
| NEU | org.springframework.boot:spring-boot-starter-data-jpa | BOM/Parent | compile | Neue Laufzeit-/Testabhängigkeit; transitiv, lizenz- und security-seitig prüfen. |
| NEU | org.springframework.boot:spring-boot-starter-test | BOM/Parent | test | Neue Laufzeit-/Testabhängigkeit; transitiv, lizenz- und security-seitig prüfen. |
| GEÄNDERT | com.h2database:h2 | BOM/Parent → BOM/Parent | compile → runtime | Gemeinsame Dependency; Version und Scope im effektiven POM prüfen. |
Die folgenden Ausschnitte stammen direkt aus den enthaltenen Projekten. Dadurch ist sichtbar, welche Verantwortung tatsächlich verschoben wurde.
Migrationsbedeutung: javax Entity wird Jakarta Entity.
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;
}
}
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;
}
}
--- 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;
}
Migrationsbedeutung: Session API/Unit of Work wird Spring Data Repository.
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();
}
}
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> {
}
--- 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> {
}
Migrationsbedeutung: Konfiguration wechselt auf Spring Boot/JPA.
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;
}
}
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);
}
}
--- 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);
}
}
| ID | Ebene | Prüfung | erforderlicher Nachweis |
|---|---|---|---|
| 23-HIBERNATE-JPA-A01 | Integration/Contract | Schema-Diff enthält keine ungewollten Änderungen. | Automatisierter Test und CI-Protokoll |
| 23-HIBERNATE-JPA-A02 | Integration/Contract | Bestehende IDs und Beziehungen bleiben lesbar. | Automatisierter Test und CI-Protokoll |
| 23-HIBERNATE-JPA-A03 | Integration/Contract | Transaktionen schließen vor API-Serialisierung sauber ab. | Automatisierter Test und CI-Protokoll |
| 23-HIBERNATE-JPA-A04 | Integration/Contract | Keine LazyInitializationException im vorgesehenen Pfad. | Automatisierter Test und CI-Protokoll |
| 23-HIBERNATE-JPA-A05 | Integration/Contract | Query-Anzahl/Performance ist akzeptiert. | Automatisierter Test und CI-Protokoll |
| 23-HIBERNATE-JPA-T01 | Unit | Fachlogik ohne Container oder externen Dienst testen. | Unit-Test |
| 23-HIBERNATE-JPA-T02 | Regression | Legacy- und Modern-Ergebnis für denselben Golden-Master-Vektor vergleichen. | Vergleichsreport |
| 23-HIBERNATE-JPA-T03 | Negative | Fehlerhafte, leere und grenzwertige Eingaben prüfen. | Negativtest |
| 23-HIBERNATE-JPA-T04 | Operations | Start, Health, Shutdown und Konfigurationsfehler prüfen. | Deployment-/Startprotokoll |
| 23-HIBERNATE-JPA-T05 | Database | Rollback, Constraints, Locking und Schema-Kompatibilität mit produktionsnahen Daten prüfen. | DB-Integrationstest |
| 23-HIBERNATE-JPA-F01 | Fokus | entity mapping, repository query, transaction and N+1 checks | Modulspezifischer Testreport |
| Risiko | Auswirkung | Gegenmaßnahme | Gate |
|---|---|---|---|
| entity lifecycle, schema and query differences | NIEDRIG | entity mapping, repository query, transaction and N+1 checks | vor Cutover |
| Neue Framework-/Library-Laufzeitabhängigkeiten | MITTEL | SBOM, Lizenz-, CVE- und transitive Dependency-Prüfung im Online-Build | vor Release |
| Legacy-Verhalten war implizit an entfernte Library gekoppelt | MITTEL | Contract-/Golden-Master-Tests und gezielte Fehlerpfade | vor Abschaltung |
Keine zusätzlichen Ressourcen.
Alte und neue Persistenzadapter können auf demselben Schema lesen; paralleles Schreiben erfordert abgestimmte Cache- und Versionierungsregeln.