Fachlicher Vertrag: unverändert zu erhalten
- SQL, Parameterbindung und Row-Mapping bleiben fachlich identisch.
- Connections und Statements werden vom Framework sicher verwaltet.
- Transaktionen liegen außerhalb des Repositorys.
Statement, ResultSet und manuelles Mapping wird nachvollziehbar in JdbcClient und RowMapper ü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 | imperativ / ohne Framework-Annotationen | explizite Java-Abstraktionen | Annotationen und Containerfunktionen werden nur dort eingesetzt, wo sie eine konkrete technische Verantwortung übernehmen. |
| Abhängigkeiten | 2 direkte Dependencies | 3 direkte Dependencies | 2 neu, 1 entfernt; Versionen und transitive Auswirkungen im erfolgreichen Online-Build prüfen. |
| Öffentliche API | 3 erkannte Methoden | 3 erkannte Methoden | Methoden werden nach fachlicher Rolle gemappt; reine Bootstrap- und Framework-Methoden sind kein fachlicher Vertrag. |
| Datenmodell | class CustomerDao | class CustomerRepository, record Customer | 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. |
SQL mapping, transaction and resource handling
row mapping, empty result, duplicate key and rollback tests
Die Zuordnung ist semantisch: Eine Legacy-Klasse kann in mehrere moderne Rollen zerlegt werden.
| Legacy-Rolle / Datei | Modern-Rolle / Datei | Bedeutung |
|---|---|---|
| CustomerDao.java | CustomerRepository.java + Customer.java | Manuelles JDBC wird JdbcClient Repository plus Row Mapping. |
| Datei | Typ | Annotationen | öffentliche API | Pattern | Zeilen |
|---|---|---|---|---|---|
| CustomerDao.java | class CustomerDao | — | void init() void save(long id, String name) Optional<String> findName(long id) | DAO | 36 |
| Datei | Typ | Annotationen | öffentliche API | Pattern | Zeilen |
|---|---|---|---|---|---|
| Customer.java | record Customer | — | — | — | 4 |
| CustomerRepository.java | class CustomerRepository | — | void init() void save(Customer c) Optional<Customer> find(long id) | Repository + Row Mapper | 27 |
| Status | Dependency | Version | Scope | Prüfung |
|---|---|---|---|---|
| ENTFERNT | org.junit.jupiter:junit-jupiter | BOM/Parent | test | Legacy-Laufzeit entfällt oder wird durch Plattform/BOM ersetzt. |
| NEU | org.springframework.boot:spring-boot-starter-jdbc | 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. |
| BEIBEHALTEN | com.h2database:h2 | BOM/Parent → BOM/Parent | compile → compile | 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: Manuelles JDBC wird JdbcClient Repository plus Row Mapping.
projects/22-spring-jdbc/legacy/src/main/java/at/aydin/lab/jdbc/legacy/CustomerDao.java
package at.aydin.lab.jdbc.legacy;
import java.sql.*;
import java.util.*;
// Design Pattern: DAO
// Zweck: Kapselt Statement-, ResultSet- und Mapping-Boilerplate.
public final class CustomerDao {
private final Connection connection;
public CustomerDao(Connection connection) {
this.connection = connection;
}
public void init() throws SQLException {
try (Statement s = connection.createStatement()) {
s.execute("create table if not exists customer(id bigint primary key,name varchar(100))");
}
}
public void save(long id, String name) throws SQLException {
try (PreparedStatement p = connection.prepareStatement("merge into customer key(id) values(?,?)")) {
p.setLong(1, id);
p.setString(2, name);
p.executeUpdate();
}
}
public Optional<String> findName(long id) throws SQLException {
try (PreparedStatement p = connection.prepareStatement("select name from customer where id=?")) {
p.setLong(1, id);
try (ResultSet r = p.executeQuery()) {
return r.next() ? Optional.of(r.getString(1)) : Optional.empty();
}
}
}
}
projects/22-spring-jdbc/modern/src/main/java/at/aydin/lab/jdbc/modern/CustomerRepository.java
package at.aydin.lab.jdbc.modern;
import org.springframework.jdbc.core.simple.JdbcClient;
import java.util.*;
// Design Pattern: Repository + Row Mapper
// Zweck: SQL bleibt explizit, Infrastruktur-Boilerplate wird vom JdbcClient übernommen.
public final class CustomerRepository {
private final JdbcClient jdbc;
public CustomerRepository(JdbcClient jdbc) {
this.jdbc = jdbc;
}
public void init() {
jdbc.sql("create table if not exists customer(id bigint primary key,name varchar(100))").update();
}
public void save(Customer c) {
jdbc.sql("merge into customer key(id) values(:id,:name)").param("id", c.id()).param("name", c.name())
.update();
}
public Optional<Customer> find(long id) {
return jdbc.sql("select id,name from customer where id=:id").param("id", id).query((rs, row) -> new Customer(rs.getLong("id"),
rs.getString("name"))).optional();
}
}
projects/22-spring-jdbc/modern/src/main/java/at/aydin/lab/jdbc/modern/Customer.java
package at.aydin.lab.jdbc.modern;
public record Customer(long id, String name) {
}
| ID | Ebene | Prüfung | erforderlicher Nachweis |
|---|---|---|---|
| 22-SPRING-JDBC-A01 | Integration/Contract | Keine SQL-Konkatenation mit Nutzereingaben. | Automatisierter Test und CI-Protokoll |
| 22-SPRING-JDBC-A02 | Integration/Contract | Row-Mapping deckt Null-/Typfälle ab. | Automatisierter Test und CI-Protokoll |
| 22-SPRING-JDBC-A03 | Integration/Contract | Leere Ergebnisse haben definierte Semantik. | Automatisierter Test und CI-Protokoll |
| 22-SPRING-JDBC-A04 | Integration/Contract | Constraint-Fehler sind stabil übersetzt. | Automatisierter Test und CI-Protokoll |
| 22-SPRING-JDBC-A05 | Integration/Contract | Repository beteiligt sich korrekt an Service-Transaktionen. | Automatisierter Test und CI-Protokoll |
| 22-SPRING-JDBC-T01 | Unit | Fachlogik ohne Container oder externen Dienst testen. | Unit-Test |
| 22-SPRING-JDBC-T02 | Regression | Legacy- und Modern-Ergebnis für denselben Golden-Master-Vektor vergleichen. | Vergleichsreport |
| 22-SPRING-JDBC-T03 | Negative | Fehlerhafte, leere und grenzwertige Eingaben prüfen. | Negativtest |
| 22-SPRING-JDBC-T04 | Operations | Start, Health, Shutdown und Konfigurationsfehler prüfen. | Deployment-/Startprotokoll |
| 22-SPRING-JDBC-T05 | Database | Rollback, Constraints, Locking und Schema-Kompatibilität mit produktionsnahen Daten prüfen. | DB-Integrationstest |
| 22-SPRING-JDBC-F01 | Fokus | row mapping, empty result, duplicate key and rollback tests | Modulspezifischer Testreport |
| Risiko | Auswirkung | Gegenmaßnahme | Gate |
|---|---|---|---|
| SQL mapping, transaction and resource handling | NIEDRIG | row mapping, empty result, duplicate key and rollback tests | 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.
Beide Datenadapter können hinter einem Repository-Port laufen; nur einer wird pro Use Case aktiviert.