Files
agenticCode/x-docs/report-jx0034n0-vs-multitableimportjob.md
2026-07-20 08:03:14 +02:00

12 KiB
Raw Blame History

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' erst C-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 immer saveWithAppendEntry(entity) und zählt immer INSRT_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-MELEM ist in Java strukturell immer 0. Fachliche Absicherung: In der Praxis läuft der Job mit Default PAR4=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_ERR wird 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 TRANSACTION alle #P-ET-MAX Sätze bzw. am Ende; BACKOUT bei Check-Only/Backout.
  • Java: Commit-Granularität kommt aus dem jBeret-Chunk/Batchlet-Framework; doBeforeEndTransaction macht 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()=false entschä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: rowMapper erkennt Header inhaltlich (erstes Feld == VGUET/VGUETSPA) und doProcessItem nutzt zusätzlich das positionale headerLineExists-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

  1. Struktur ohne Framework-Lesen rekonstruiert: callees auf MultiTableImportJob lieferte sofort EXTENDS AbstractPurBatchJob, REFERENCES auf die drei Step-Klassen und die INJECTS-Wiring — der Job→Step-Graph war ohne Durchsuchen der Batch-Basisklassen sichtbar.
  2. Vollständige Aufruf-Hülle des Natural-Programms: call-tree enumerierte die 46-Modul-Closure inkl. Access-Layer (YELEMMN0, YMTABMN0), Validierungs-Subprogramme (USIX004N, ISI173N0) und Batch-Monitoring (VBATCHN0/N1) — die Grundlage, um jedes CALLNAT einer Java-Logic-Klasse zuzuordnen.
  3. DB-Zugriff durch Indirektion aufgelöst: db-accesses?depth=3 fand VERSVW_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.
  4. SQL-Text im Klartext: sql-statements?depth=3 lieferte die konkreten SELECT … FROM VERSVW_ELEMENTOS ….
  5. Subroutinen-Inventar für 1:1-Mapping: module_context.functions gab die 13 Natural-Subroutinen, gegen die die 8 Java-Methoden gemappt wurden.
  6. Cross-Projekt-Suche: search/value?value=JX0034N0 bestä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=Y Delete-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.