ABAP TRY CATCH ENDTRY: Exception Handling und Fehlerbehandlung

kategorie
ABAP-Statements
Veröffentlicht
autor
Johannes

Die TRY...CATCH...ENDTRY-Struktur ist das moderne Konzept zur Fehlerbehandlung in ABAP. Sie ermöglicht es, Laufzeitfehler (Exceptions) abzufangen und kontrolliert darauf zu reagieren, anstatt dass das Programm abstürzt.

Grundkonzept

  • Exception: Ein Fehlerobjekt, das bei einem Problem ausgelöst wird
  • TRY-Block: Code, der Exceptions auslösen könnte
  • CATCH-Block: Code, der die Exception behandelt
  • CLEANUP-Block: Code, der immer ausgeführt wird (Aufräumarbeiten)

Syntax

Grundstruktur

TRY.
" Code, der Exceptions auslösen kann
CATCH <exception_class> [INTO <referenz>].
" Fehlerbehandlung
[CATCH <andere_exception> INTO <referenz>.]
" Weitere Fehlerbehandlung
[CLEANUP.
" Aufräumarbeiten (optional)]
ENDTRY.

Exception auslösen

RAISE EXCEPTION TYPE <exception_class>
[EXPORTING <parameter> = <wert>].

Systemfelder

Nach einem CATCH:

  • Die Exception-Referenz enthält Details zum Fehler
  • sy-subrc wird nicht gesetzt (ist für klassische Fehlerbehandlung)

Beispiele

1. Einfaches TRY-CATCH

DATA: lv_result TYPE i.
TRY.
lv_result = 10 / 0. " Division durch Null
CATCH cx_sy_zerodivide.
WRITE: / 'Fehler: Division durch Null!'.
ENDTRY.
WRITE: / 'Programm läuft weiter.'.

2. Exception-Details auslesen

DATA: lx_error TYPE REF TO cx_sy_zerodivide.
TRY.
DATA(lv_result) = 10 / 0.
CATCH cx_sy_zerodivide INTO lx_error.
" Fehlermeldung auslesen
DATA(lv_message) = lx_error->get_text( ).
WRITE: / 'Fehler:', lv_message.
" Weitere Informationen
DATA(lv_longtext) = lx_error->get_longtext( ).
ENDTRY.

3. Mehrere Exception-Typen behandeln

DATA: lv_number TYPE i,
lv_text TYPE string VALUE 'ABC'.
TRY.
lv_number = lv_text. " Konvertierungsfehler
CATCH cx_sy_conversion_no_number INTO DATA(lx_conv).
WRITE: / 'Konvertierungsfehler:', lx_conv->get_text( ).
CATCH cx_sy_zerodivide INTO DATA(lx_div).
WRITE: / 'Division durch Null:', lx_div->get_text( ).
CATCH cx_root INTO DATA(lx_any).
" Fängt alle anderen Exceptions
WRITE: / 'Allgemeiner Fehler:', lx_any->get_text( ).
ENDTRY.

4. Exception-Hierarchie (cx_root)

Alle ABAP-Exceptions erben von cx_root. Die Hierarchie:

cx_root (Basisklasse)
├── cx_static_check (Muss deklariert oder gefangen werden)
├── cx_dynamic_check (Sollte gefangen werden)
└── cx_no_check (Schwere Fehler, meist nicht fangbar)
TRY.
" Irgendein Code
CATCH cx_static_check INTO DATA(lx_static).
" Fängt alle statisch prüfbaren Exceptions
CATCH cx_dynamic_check INTO DATA(lx_dynamic).
" Fängt alle dynamisch prüfbaren Exceptions
CATCH cx_root INTO DATA(lx_all).
" Fängt wirklich alle Exceptions
ENDTRY.

5. Exception in Methode auslösen und deklarieren

CLASS lcl_validator DEFINITION.
PUBLIC SECTION.
METHODS: validate_age IMPORTING iv_age TYPE i
RAISING cx_parameter_invalid.
ENDCLASS.
CLASS lcl_validator IMPLEMENTATION.
METHOD validate_age.
IF iv_age < 0 OR iv_age > 150.
RAISE EXCEPTION TYPE cx_parameter_invalid
EXPORTING
parameter = 'AGE'.
ENDIF.
ENDMETHOD.
ENDCLASS.
" Verwendung
DATA: lo_validator TYPE REF TO lcl_validator.
lo_validator = NEW #( ).
TRY.
lo_validator->validate_age( -5 ).
WRITE: / 'Alter ist gültig.'.
CATCH cx_parameter_invalid INTO DATA(lx_invalid).
WRITE: / 'Ungültiger Parameter:', lx_invalid->get_text( ).
ENDTRY.

6. Eigene Exception-Klasse erstellen

" Exception-Klasse definieren
CLASS lcx_customer_not_found DEFINITION
INHERITING FROM cx_static_check.
PUBLIC SECTION.
INTERFACES: if_t100_message.
METHODS: constructor IMPORTING iv_customer_id TYPE i
!previous TYPE REF TO cx_root OPTIONAL,
get_customer_id RETURNING VALUE(rv_id) TYPE i.
DATA: mv_customer_id TYPE i READ-ONLY.
ENDCLASS.
CLASS lcx_customer_not_found IMPLEMENTATION.
METHOD constructor.
super->constructor( previous = previous ).
mv_customer_id = iv_customer_id.
ENDMETHOD.
METHOD get_customer_id.
rv_id = mv_customer_id.
ENDMETHOD.
ENDCLASS.
" Verwendung
CLASS lcl_customer_service DEFINITION.
PUBLIC SECTION.
METHODS: get_customer IMPORTING iv_id TYPE i
RETURNING VALUE(rs_customer) TYPE zcustomer
RAISING lcx_customer_not_found.
ENDCLASS.
CLASS lcl_customer_service IMPLEMENTATION.
METHOD get_customer.
SELECT SINGLE * FROM zcustomer
WHERE id = @iv_id
INTO @rs_customer.
IF sy-subrc <> 0.
RAISE EXCEPTION TYPE lcx_customer_not_found
EXPORTING
iv_customer_id = iv_id.
ENDIF.
ENDMETHOD.
ENDCLASS.
" Aufruf
DATA: lo_service TYPE REF TO lcl_customer_service.
lo_service = NEW #( ).
TRY.
DATA(ls_customer) = lo_service->get_customer( 12345 ).
WRITE: / 'Kunde gefunden:', ls_customer-name.
CATCH lcx_customer_not_found INTO DATA(lx_not_found).
WRITE: / 'Kunde nicht gefunden. ID:', lx_not_found->get_customer_id( ).
ENDTRY.

7. CLEANUP für Aufräumarbeiten

Der CLEANUP-Block wird ausgeführt, wenn eine Exception den TRY-Block verlässt:

DATA: lo_file TYPE REF TO cl_file.
TRY.
" Datei öffnen
lo_file = NEW #( 'data.txt' ).
lo_file->open( ).
" Verarbeitung (kann Exception auslösen)
lo_file->read( ).
CATCH cx_file_error INTO DATA(lx_file).
WRITE: / 'Dateifehler:', lx_file->get_text( ).
CLEANUP.
" Wird IMMER ausgeführt, auch bei Exception
IF lo_file IS BOUND.
lo_file->close( ).
ENDIF.
ENDTRY.

8. Verschachtelte TRY-Blöcke

TRY.
TRY.
DATA(lv_result) = 10 / 0.
CATCH cx_sy_zerodivide.
WRITE: / 'Innerer Fehler: Division durch Null'.
" Exception weiterreichen
RAISE EXCEPTION TYPE cx_sy_arithmetic_error.
ENDTRY.
CATCH cx_sy_arithmetic_error INTO DATA(lx_math).
WRITE: / 'Äußerer Fehler:', lx_math->get_text( ).
ENDTRY.

9. RETRY - Wiederholung nach Fehler

Mit RETRY kann der TRY-Block wiederholt werden:

DATA: lv_attempts TYPE i VALUE 0,
lv_success TYPE abap_bool VALUE abap_false.
TRY.
lv_attempts = lv_attempts + 1.
WRITE: / 'Versuch:', lv_attempts.
" Simulierter Fehler in den ersten 2 Versuchen
IF lv_attempts < 3.
RAISE EXCEPTION TYPE cx_sy_dyn_call_error.
ENDIF.
lv_success = abap_true.
WRITE: / 'Erfolgreich!'.
CATCH cx_sy_dyn_call_error.
IF lv_attempts < 3.
WRITE: / 'Fehler - neuer Versuch...'.
RETRY. " Springt zurück zum TRY
ELSE.
WRITE: / 'Maximale Versuche erreicht.'.
ENDIF.
ENDTRY.

10. Exception mit RESUMABLE (Fortsetzbar)

CLASS lcx_warning DEFINITION
INHERITING FROM cx_static_check.
PUBLIC SECTION.
METHODS constructor.
ENDCLASS.
CLASS lcx_warning IMPLEMENTATION.
METHOD constructor.
super->constructor( ).
ENDMETHOD.
ENDCLASS.
CLASS lcl_processor DEFINITION.
PUBLIC SECTION.
METHODS: process RAISING RESUMABLE(lcx_warning).
ENDCLASS.
CLASS lcl_processor IMPLEMENTATION.
METHOD process.
WRITE: / 'Verarbeitung startet...'.
RAISE RESUMABLE EXCEPTION TYPE lcx_warning.
WRITE: / 'Verarbeitung fortgesetzt!'.
ENDMETHOD.
ENDCLASS.
" Verwendung
DATA: lo_proc TYPE REF TO lcl_processor.
lo_proc = NEW #( ).
TRY.
lo_proc->process( ).
CATCH BEFORE UNWIND lcx_warning.
WRITE: / 'Warnung erhalten - fortsetzen mit RESUME'.
RESUME. " Setzt Ausführung nach RAISE fort
ENDTRY.

Standard-Exception-Klassen

KlasseBeschreibung
cx_sy_zerodivideDivision durch Null
cx_sy_arithmetic_errorArithmetische Fehler
cx_sy_conversion_no_numberString zu Zahl Konvertierung
cx_sy_range_out_of_boundsArray-Index außerhalb
cx_sy_itab_line_not_foundTabellenzeile nicht gefunden
cx_sy_ref_is_initialDereferenzierung von NULL
cx_sy_move_cast_errorUngültiger Downcast
cx_sy_dyn_call_errorDynamischer Aufruffehler

Exception-Behandlung in Methoden

CLASS lcl_example DEFINITION.
PUBLIC SECTION.
" Exception muss deklariert werden (cx_static_check)
METHODS: method_with_exception
RAISING cx_parameter_invalid.
" Exception muss NICHT deklariert werden (cx_dynamic_check)
METHODS: method_with_runtime_error.
" Exception weiterreichen
METHODS: wrapper_method
RAISING cx_parameter_invalid.
ENDCLASS.
CLASS lcl_example IMPLEMENTATION.
METHOD method_with_exception.
RAISE EXCEPTION TYPE cx_parameter_invalid.
ENDMETHOD.
METHOD method_with_runtime_error.
DATA(lv_x) = 1 / 0. " cx_sy_zerodivide (dynamic_check)
ENDMETHOD.
METHOD wrapper_method.
" Exception wird weitergereicht
method_with_exception( ).
ENDMETHOD.
ENDCLASS.

TRY-CATCH vs. Klassische Fehlerbehandlung

AspektTRY-CATCHsy-subrc
TypObjektorientiertProzedural
InformationDetaillierte ExceptionNur Returncode
WeitergabeAutomatisch nach obenManuell prüfen
EmpfehlungModern, bevorzugtLegacy, für Open SQL

Wichtige Hinweise / Best Practice

  • Fangen Sie nur Exceptions, die Sie sinnvoll behandeln können.
  • Verwenden Sie spezifische Exception-Klassen, nicht nur cx_root.
  • Erstellen Sie eigene Exception-Klassen für domänenspezifische Fehler.
  • Nutzen Sie INTO um Fehlerdetails auszulesen.
  • Der CLEANUP-Block ist wichtig für Ressourcenfreigabe.
  • Deklarieren Sie Exceptions in Methodensignaturen mit RAISING.
  • cx_static_check erfordert explizite Behandlung oder Deklaration.
  • Loggen Sie Exceptions für Debugging und Monitoring.
  • Kombinieren Sie mit CLASS und INTERFACE für saubere Architektur.
  • Nach Datenbankoperationen mit SELECT prüfen Sie weiterhin sy-subrc.