12 KiB
Funktionsvergleich: JX0034N0 (Natural, upms) ↔ MultiTableImportJob (Java, pur)
Erstellt: 2026-07-19 · Server: agenticcode v103 · Analyse-Basis: REST-API (MCP-Session-Fehler → Fallback laut CLAUDE.md)
Beide Programme laden eine multiple Schlüsseltabelle (VERSIS-„MELE"-Einträge, Tabellen VGUET/VGUETSPA) aus
einem Workfile/CSV in die DB. Der Java-Job ist die Migration des Natural-Subprogramms; die Herkunft ist im Code als
Kommentar // JX0034N0.nat und Konstante PROGRAM_IDENTIFIER = "JX0034N0" festgehalten.
1. Strukturelle Zuordnung (bestätigt via API)
| Natural (Subroutine) | Java (Klasse / Methode) | Status |
|---|---|---|
INIT-PROCESSING |
MultiTableImportInitStep.doProcess() |
✅ |
CHECK-PARMS |
MultiTableImportInitStep.checkParamsImpl() |
✅ |
MAIN-PROCESSING (Orchestr.) |
MultiTableImportJob.jobSteps() (init → processing → end) |
✅ |
MAIN-PART (READ WORK-Loop) |
MultiTableImportProcessingStep (Reader + doProcessItem) |
✅ |
HEADER |
MultiTableImportProcessingStep.processHeader() |
✅ |
DEL-MELEM |
MultiTableImportProcessingStep.performDelete() |
⚠️ (Fehlerbehandlung fehlt) |
LOAD-MELEM |
MultiTableImportProcessingStep.performLoad() |
⚠️ (kein UPDATE) |
FILL-VGUET |
— (in Natural auskommentiert; Java hat kein Äquivalent) | ✅ Parität |
FILL-RESULTS/WRITE-RESULTS |
fillResults() / writeResults() |
✅ |
ET-PROCESSING |
doBeforeEndTransaction() + Framework-Chunk-Commit |
⚠️ (andere Commit-Kadenz) |
END-PROCESSING |
MultiTableImportEndStep.doProcess() |
✅ |
WRITE-MELE-DATA |
writeMeleData() |
✅ |
Client-Validierung CALLNAT USIX004N → ClientValidationLogic.validate(),
Branch-Prüfung CALLNAT ISI173N0 → FieldRecoveryLogic.findAlternativeKey(),
Batch-Monitoring CALLNAT VBATCHN0 (BEGIN/END) → PurBatchLogic.begin() — jeweils funktional äquivalent.
Fazit Struktur: Der Kontrollfluss ist 1:1 abgebildet. Die Steuerparameter (PAR3 #JP-CHECK-ONLY,
PAR4 #JP-DEL als Y/N-Schalter), die VGUET/VGUETSPA-Filterung, die Anteils-Summenprüfung (=100 je Gruppe) und die
zehn Statistik-Zähler sind vollständig übernommen.
2. Funktionale Unterschiede
🔴 U1 — Kein In-Place-UPDATE (Upsert verloren) — verhaltensrelevant
- Natural
LOAD-MELEM(Zeile 590–619): bei#JP-DEL='N'erstC-MOD-GET; wenn Satz existiert →C-MOD-UPD(#C-UPD-MELEM++), sonst →C-MOD-ADD(#C-INSRT-MELEM++). Echter Upsert über den MELE-Schlüssel. - Java
doWriteItems(Zeile 74–83): ruft immersaveWithAppendEntry(entity)und zählt immerINSRT_MELEM. Der UPDATE-Pfad ist bewusst weggelassen — Kommentar im Code: „Legacy code has logic for update when #JP-DEL = 'N', but it doesn't function properly due to MultiTableEntryEntity not having a unique key outside vid." - Konsequenz: Ein erneuter Lauf ohne vorheriges Löschen erzeugt in Java Duplikate statt Änderungen.
Der Zähler
#C-UPD-MELEMist in Java strukturell immer0. Fachliche Absicherung: In der Praxis läuft der Job mit DefaultPAR4=Y(Delete), wodurch vorher alles gelöscht wird und Insert-only korrekt ist — aber die#JP-DEL='N'-Semantik ist nicht erhalten.
🟠 U2 — Delete-Fehlerbehandlung nicht implementiert
- Natural
DEL-MELEM(Zeile 471–477): bei Löschfehler →#L-BACKOUT := TRUE,#C-DELETE-MELEM-ERR++, Abbruch der laufenden Verarbeitung. - Java
performDelete(Zeile 257–267):// TODO CSA Handle individual errors when deleting L.471.DELETE_MELEM_ERRwird nie hochgezählt, kein Backout bei fehlgeschlagenem Löschen. - Konsequenz: Ein Teil-Löschfehler bleibt in Java unbemerkt; der Job läuft weiter und committet ggf. inkonsistent.
🟠 U3 — numAdditionalAttri 1 → 2 (bewusste Abweichung)
- Natural (Zeile 568):
C#ADDITIONAL-ATTRIBUTE-VALUES := 1(mit Alt-Kommentar#02 ??? := 3). - Java (Zeile 227):
setNumAdditionalAttri(2)— Kommentar: „Legacy code sets this to 1, but DB has it with 2". - Absichtliche Daten-Korrektur, aber eine bewusste inhaltliche Abweichung vom Original.
🟡 U4 — Commit-/Transaktions-Kadenz
- Natural
ET-PROCESSING:END TRANSACTIONalle#P-ET-MAXSätze bzw. am Ende;BACKOUTbei Check-Only/Backout. - Java: Commit-Granularität kommt aus dem jBeret-Chunk/Batchlet-Framework;
doBeforeEndTransactionmacht nur den Rollback bei Check-Only/Backout. Die feinkörnige#P-ET-MAX-Batchung existiert nicht mehr. - Konsequenz: Endzustand (voller Commit bzw. voller Rollback) ist äquivalent, aber Zwischen-Commit-Punkte und damit
Restart-/Recovery-Verhalten unterscheiden sich.
isRestartable()=falseentschärft das.
🟡 U5 — Serialisierung der Zusatzattribute
- Natural: setzt gezielte Occurrences (
VAL-NUMERIC-ADD-ATTR(1)=Branche,(2)=Anteil,VAL-ALFANUMERIC-ADD-ATTR(1)=Langtext). - Java
fillNewVGUETSPAEntry(Zeile 234–248): baut einen gepackten String über 10 Slot-Paare (18-stellig numerisch + 50-stellig alpha). Repräsentativ vermutlich deckungsgleich mit dem gepackten DB-Format, aber nicht trivial gleich — sollte gegen echte DB-Werte verifiziert werden.
🟡 U6 — Header-Erkennung positional vs. inhaltlich
- Natural: Zeile 1 ist immer Header (
#L-HEADER-LINE-Flag). - Java:
rowMappererkennt Header inhaltlich (erstes Feld ==VGUET/VGUETSPA) unddoProcessItemnutzt zusätzlich das positionaleheaderLineExists-Flag. Konvergiert in der Praxis, ist aber ein doppelter Mechanismus mit theoretischem Abweichungspotenzial.
Äquivalent bestätigt (keine Abweichung): Parameter-Check (Y/N-Validierung + Fehlerslot), Client-/Branch-Validierung,
VGUET-vs-VGUETSPA (beide bauen nur SPA — Natural-FILL-VGUET ist auskommentiert), Check-Only-Backout,
Gruppenwechsel-Summenprüfung, Statistik-Ausgabe, End-/Error-Reporting.
3. DB-Wirkung (via API ermittelt)
| Seite | Ziel-Tabelle (API) | Zugriffsweg |
|---|---|---|
| Natural | VERSVW_ELEMENTOS (READ), VDB2-VERSIS_ELEMENTOS (WRITE) |
nur transitiv über Access-Layer YELEMMN0 (db-accesses?depth=3, via:"YELEMMN0") |
| Java | multi_table_entry (MultiTableEntryEntity → @Table, Mode DECLARES) |
über MultiTableEntryLogic |
Beide adressieren dieselbe fachliche Entität (VERSIS-Multelem). Ein direkter Tabellen-für-Tabelle-Abgleich ist aktuell nur manuell möglich (siehe Verbesserung V3).
4. Wie die agentic API bei dieser Analyse geholfen hat
- Struktur ohne Framework-Lesen rekonstruiert:
calleesaufMultiTableImportJoblieferte sofortEXTENDS AbstractPurBatchJob,REFERENCESauf die drei Step-Klassen und dieINJECTS-Wiring — der Job→Step-Graph war ohne Durchsuchen der Batch-Basisklassen sichtbar. - Vollständige Aufruf-Hülle des Natural-Programms:
call-treeenumerierte die 46-Modul-Closure inkl. Access-Layer (YELEMMN0,YMTABMN0), Validierungs-Subprogramme (USIX004N,ISI173N0) und Batch-Monitoring (VBATCHN0/N1) — die Grundlage, um jedesCALLNATeiner Java-Logic-Klasse zuzuordnen. - DB-Zugriff durch Indirektion aufgelöst:
db-accesses?depth=3fandVERSVW_ELEMENTOS/VDB2-VERSIS_ELEMENTOS, obwohl der Job direkt kein SQL enthält (depth=0= leer). Die Provenienz (via:"YELEMMN0",viaCopycode,includedAt) zeigte exakt, über welche Copycode-/Access-Layer-Kette der Zugriff läuft — das findet ein grep nicht. - SQL-Text im Klartext:
sql-statements?depth=3lieferte die konkretenSELECT … FROM VERSVW_ELEMENTOS …. - Subroutinen-Inventar für 1:1-Mapping:
module_context.functionsgab die 13 Natural-Subroutinen, gegen die die 8 Java-Methoden gemappt wurden. - Cross-Projekt-Suche:
search/value?value=JX0034N0bestätigte den Herkunfts-Anker im Java-Code.
5. Verbesserungsmöglichkeiten der statischen Analyse
V1 — Cross-Projekt-„migrated-from"-Verknüpfung (größter Hebel)
Der Java-Code trägt den Anker (PROGRAM_IDENTIFIER="JX0034N0", Kommentar // JX0034N0.nat), aber die API kennt keine
projektübergreifende Kante Java↔Natural. search/value in upms nach MultiTableImportJob = leer. Ein Enricher, der
solche Identifier/Kommentar-Marker indexiert und eine MIGRATED_FROM-Beziehung (pur-Modul → upms-Modul) exponiert,
würde einen Agenten direkt vom Java-Job zur Natural-Quelle springen lassen — genau der Sprung, den ich hier manuell
gebaut habe.
V2 — followWiring-Call-Tree gegen CHA-Explosion absichern
call-tree?followWiring=true&depth=6 auf MultiTableImportJob lief in Timeout (CHA-Over-Approximation der
INJECTS/REFERENCES-Fan-outs). Nötig: Default-Tiefenbegrenzung, ein „collapse over-approximation"-Flag oder gestreamte
Ausgabe, damit der wichtigste Java-Traversal-Modus nutzbar bleibt.
V3 — DB-Wirkung über die Java-Logic-/Repository-Schicht propagieren
db-accesses?depth=4 auf MultiTableImportProcessingStep = leer, obwohl der Step über
MultiTableEntryLogic.saveWithAppendEntry schreibt. Die Entity→Tabelle-Kante existiert (multi_table_entry,
DECLARES), aber es gibt keine READS/WRITES-Propagation Step→Logic→Repository→@Table. Würde der Java-Analyzer
Repository-/save/delete-Aufrufe zu READS/WRITES auf die Entity-Tabelle auflösen, wäre der DB-Effekt beider Seiten
direkt vergleichbar (Natural VERSVW_ELEMENTOS ↔ Java multi_table_entry).
V4 — Semantische Diff-Unterstützung
Ein „side-effect summary" pro Modul (welche Kontext-/Zähler-Felder wo geschrieben werden — via field_flow) würde den
manuellen Abgleich der zehn Statistik-Zähler und Flags (#L-BACKOUT, #SUMME-ANT) automatisieren.
6. Gesamtbewertung
MultiTableImportJob bildet JX0034N0 funktional weitgehend korrekt ab — Kontrollfluss, Parameter-Semantik, Validierungen, Filterung, Summenprüfung und Statistik stimmen überein. Es gibt jedoch echte, teils bewusste Abweichungen, die dokumentiert gehören:
- U1 (kein UPDATE / immer Insert) und U2 (fehlende Delete-Fehlerbehandlung) sind die einzigen mit potenziell
fachlicher Auswirkung. Beide sind im Java-Code als bewusste Einschränkung bzw. TODO markiert und durch den
Default-Betrieb (
PAR4=YDelete-then-Insert) praktisch entschärft — aber die#JP-DEL='N'-Semantik ist nicht vollständig migriert. - U3 (numAdditionalAttri=2) ist eine absichtliche Daten-Korrektur gegenüber dem Original.
- U4/U5/U6 sind Framework-/Repräsentations-Unterschiede ohne erwarteten Endzustands-Effekt, sollten aber gegen Echtdaten (U5) verifiziert werden.