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-subrcwird 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 ExceptionsENDTRY.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.
" VerwendungDATA: 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 definierenCLASS 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.
" VerwendungCLASS 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.
" AufrufDATA: 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.
" VerwendungDATA: 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 fortENDTRY.Standard-Exception-Klassen
| Klasse | Beschreibung |
|---|---|
cx_sy_zerodivide | Division durch Null |
cx_sy_arithmetic_error | Arithmetische Fehler |
cx_sy_conversion_no_number | String zu Zahl Konvertierung |
cx_sy_range_out_of_bounds | Array-Index außerhalb |
cx_sy_itab_line_not_found | Tabellenzeile nicht gefunden |
cx_sy_ref_is_initial | Dereferenzierung von NULL |
cx_sy_move_cast_error | Ungültiger Downcast |
cx_sy_dyn_call_error | Dynamischer 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
| Aspekt | TRY-CATCH | sy-subrc |
|---|---|---|
| Typ | Objektorientiert | Prozedural |
| Information | Detaillierte Exception | Nur Returncode |
| Weitergabe | Automatisch nach oben | Manuell prüfen |
| Empfehlung | Modern, bevorzugt | Legacy, 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
INTOum Fehlerdetails auszulesen. - Der
CLEANUP-Block ist wichtig für Ressourcenfreigabe. - Deklarieren Sie Exceptions in Methodensignaturen mit
RAISING. cx_static_checkerfordert explizite Behandlung oder Deklaration.- Loggen Sie Exceptions für Debugging und Monitoring.
- Kombinieren Sie mit
CLASSundINTERFACEfür saubere Architektur. - Nach Datenbankoperationen mit
SELECTprüfen Sie weiterhinsy-subrc.