Exception Classes ermöglichen strukturierte Fehlerbehandlung in ABAP. Eigene Ausnahmen (CX_-Klassen) transportieren Fehlerdaten, unterstützen Mehrsprachigkeit und integrieren sich nahtlos in TRY-CATCH.
Exception-Hierarchie
CX_ROOT │ ├── CX_STATIC_CHECK " Muss deklariert werden (RAISING) │ └── CX_DYNAMIC_CHECK " Kann, muss aber nicht deklariert werden │ └── CX_NO_CHECK " Muss nicht deklariert werden (Runtime)| Basisklasse | RAISING nötig | Typischer Einsatz |
|---|---|---|
CX_STATIC_CHECK | Ja | Business-Exceptions |
CX_DYNAMIC_CHECK | Optional | Technische Fehler |
CX_NO_CHECK | Nein | Kritische Systemfehler |
Exception-Klasse erstellen
1. Einfache Exception (SE24/ADT)
CLASS zcx_validation_error DEFINITION PUBLIC INHERITING FROM cx_static_check CREATE PUBLIC.
PUBLIC SECTION. " Interface für Texte INTERFACES: if_t100_message. INTERFACES: if_t100_dyn_msg.
" Konstanten für Nachrichtentexte CONSTANTS: BEGIN OF invalid_input, msgid TYPE symsgid VALUE 'ZMSG', msgno TYPE symsgno VALUE '001', attr1 TYPE scx_attrname VALUE 'MV_FIELD', attr2 TYPE scx_attrname VALUE '', attr3 TYPE scx_attrname VALUE '', attr4 TYPE scx_attrname VALUE '', END OF invalid_input.
" Attribute für Fehlerdaten DATA: mv_field TYPE string READ-ONLY.
" Konstruktor METHODS: constructor IMPORTING textid LIKE if_t100_message=>t100key OPTIONAL previous LIKE previous OPTIONAL iv_field TYPE string OPTIONAL.
ENDCLASS.
CLASS zcx_validation_error IMPLEMENTATION. METHOD constructor. super->constructor( previous = previous ).
mv_field = iv_field.
CLEAR me->textid. IF textid IS INITIAL. if_t100_message~t100key = invalid_input. ELSE. if_t100_message~t100key = textid. ENDIF. ENDMETHOD.ENDCLASS.2. Exception mit mehreren Texten
CLASS zcx_order_error DEFINITION PUBLIC INHERITING FROM cx_static_check CREATE PUBLIC.
PUBLIC SECTION. INTERFACES: if_t100_message.
" Verschiedene Fehlertexte CONSTANTS: BEGIN OF order_not_found, msgid TYPE symsgid VALUE 'ZORDER', msgno TYPE symsgno VALUE '001', attr1 TYPE scx_attrname VALUE 'MV_ORDER_ID', attr2 TYPE scx_attrname VALUE '', attr3 TYPE scx_attrname VALUE '', attr4 TYPE scx_attrname VALUE '', END OF order_not_found,
BEGIN OF order_already_shipped, msgid TYPE symsgid VALUE 'ZORDER', msgno TYPE symsgno VALUE '002', attr1 TYPE scx_attrname VALUE 'MV_ORDER_ID', attr2 TYPE scx_attrname VALUE 'MV_SHIP_DATE', attr3 TYPE scx_attrname VALUE '', attr4 TYPE scx_attrname VALUE '', END OF order_already_shipped,
BEGIN OF invalid_quantity, msgid TYPE symsgid VALUE 'ZORDER', msgno TYPE symsgno VALUE '003', attr1 TYPE scx_attrname VALUE 'MV_QUANTITY', attr2 TYPE scx_attrname VALUE 'MV_MAX_QUANTITY', attr3 TYPE scx_attrname VALUE '', attr4 TYPE scx_attrname VALUE '', END OF invalid_quantity.
" Attribute DATA: mv_order_id TYPE string READ-ONLY, mv_ship_date TYPE d READ-ONLY, mv_quantity TYPE i READ-ONLY, mv_max_quantity TYPE i READ-ONLY.
METHODS: constructor IMPORTING textid LIKE if_t100_message=>t100key OPTIONAL previous LIKE previous OPTIONAL iv_order_id TYPE string OPTIONAL iv_ship_date TYPE d OPTIONAL iv_quantity TYPE i OPTIONAL iv_max_quantity TYPE i OPTIONAL.
ENDCLASS.
CLASS zcx_order_error IMPLEMENTATION. METHOD constructor. super->constructor( previous = previous ).
mv_order_id = iv_order_id. mv_ship_date = iv_ship_date. mv_quantity = iv_quantity. mv_max_quantity = iv_max_quantity.
CLEAR me->textid. IF textid IS INITIAL. if_t100_message~t100key = order_not_found. ELSE. if_t100_message~t100key = textid. ENDIF. ENDMETHOD.ENDCLASS.3. Exception werfen (RAISE EXCEPTION)
CLASS lcl_order_service DEFINITION. PUBLIC SECTION. METHODS: get_order IMPORTING iv_order_id TYPE string RETURNING VALUE(rs_order) TYPE ty_order RAISING zcx_order_error.
METHODS: ship_order IMPORTING iv_order_id TYPE string RAISING zcx_order_error.ENDCLASS.
CLASS lcl_order_service IMPLEMENTATION. METHOD get_order. " Bestellung suchen SELECT SINGLE * FROM ztorders WHERE order_id = @iv_order_id INTO @rs_order.
IF sy-subrc <> 0. RAISE EXCEPTION TYPE zcx_order_error EXPORTING textid = zcx_order_error=>order_not_found iv_order_id = iv_order_id. ENDIF. ENDMETHOD.
METHOD ship_order. DATA: ls_order TYPE ty_order.
ls_order = get_order( iv_order_id ).
IF ls_order-status = 'SHIPPED'. RAISE EXCEPTION TYPE zcx_order_error EXPORTING textid = zcx_order_error=>order_already_shipped iv_order_id = iv_order_id iv_ship_date = ls_order-ship_date. ENDIF.
" Versand durchführen... ENDMETHOD.ENDCLASS.4. Exception fangen und verarbeiten
DATA: lo_service TYPE REF TO lcl_order_service, ls_order TYPE ty_order.
lo_service = NEW #( ).
TRY. ls_order = lo_service->get_order( '12345' ).
CATCH zcx_order_error INTO DATA(lx_error). " Fehlertext ausgeben DATA(lv_message) = lx_error->get_text( ). WRITE: / 'Fehler:', lv_message.
" Auf spezifische Attribute zugreifen WRITE: / 'Order ID:', lx_error->mv_order_id.
" Fehler weiterwerfen " RAISE EXCEPTION lx_error.ENDTRY.5. Exception-Kette (PREVIOUS)
METHOD process_order. TRY. " Datenbank-Operation validate_order( is_order ).
CATCH zcx_validation_error INTO DATA(lx_val). " Ursprüngliche Exception als PREVIOUS übergeben RAISE EXCEPTION TYPE zcx_order_error EXPORTING textid = zcx_order_error=>order_not_found previous = lx_val iv_order_id = is_order-id. ENDTRY.ENDMETHOD.
" Beim Fangen: Kette durchlaufenTRY. process_order( ls_order ).
CATCH cx_root INTO DATA(lx_error). " Alle Exceptions in der Kette ausgeben DATA(lx_current) = lx_error.
WHILE lx_current IS BOUND. WRITE: / lx_current->get_text( ). lx_current = lx_current->previous. ENDWHILE.ENDTRY.6. Exception ohne Nachrichtenklasse
CLASS zcx_simple_error DEFINITION PUBLIC INHERITING FROM cx_static_check CREATE PUBLIC.
PUBLIC SECTION. " Einfacher Text ohne T100 DATA: mv_message TYPE string READ-ONLY.
METHODS: constructor IMPORTING iv_message TYPE string OPTIONAL previous LIKE previous OPTIONAL.
METHODS: if_message~get_text REDEFINITION.
ENDCLASS.
CLASS zcx_simple_error IMPLEMENTATION. METHOD constructor. super->constructor( previous = previous ). mv_message = iv_message. ENDMETHOD.
METHOD if_message~get_text. result = mv_message. ENDMETHOD.ENDCLASS.
" VerwendungRAISE EXCEPTION TYPE zcx_simple_error EXPORTING iv_message = 'Ein Fehler ist aufgetreten'.7. COND/SWITCH mit THROW
DATA: lv_age TYPE i VALUE -5.
" Exception in Ausdruck werfenDATA(lv_status) = COND string( WHEN lv_age >= 0 AND lv_age < 18 THEN 'Minderjährig' WHEN lv_age >= 18 THEN 'Erwachsen' ELSE THROW zcx_validation_error( textid = zcx_validation_error=>invalid_input iv_field = 'AGE' )).
" Mit SWITCHDATA(lv_result) = SWITCH string( lv_status WHEN 'A' THEN 'Aktiv' WHEN 'I' THEN 'Inaktiv' ELSE THROW zcx_validation_error( iv_field = 'STATUS' )).8. Lokale Exception-Klasse
" Für Tests oder lokale VerwendungCLASS lcx_local_error DEFINITION INHERITING FROM cx_static_check.
PUBLIC SECTION. DATA: mv_info TYPE string READ-ONLY.
METHODS: constructor IMPORTING iv_info TYPE string OPTIONAL previous LIKE previous OPTIONAL.
METHODS: if_message~get_text REDEFINITION.ENDCLASS.
CLASS lcx_local_error IMPLEMENTATION. METHOD constructor. super->constructor( previous = previous ). mv_info = iv_info. ENDMETHOD.
METHOD if_message~get_text. result = |Lokaler Fehler: { mv_info }|. ENDMETHOD.ENDCLASS.9. Exception-Hierarchie für Domain
" Basisklasse für alle Order-ExceptionsCLASS zcx_order_base DEFINITION PUBLIC ABSTRACT INHERITING FROM cx_static_check CREATE PUBLIC.
PUBLIC SECTION. DATA: mv_order_id TYPE string READ-ONLY.
METHODS: constructor IMPORTING iv_order_id TYPE string OPTIONAL previous LIKE previous OPTIONAL.ENDCLASS.
" Spezifische ExceptionsCLASS zcx_order_not_found DEFINITION PUBLIC INHERITING FROM zcx_order_base CREATE PUBLIC. " ...ENDCLASS.
CLASS zcx_order_locked DEFINITION PUBLIC INHERITING FROM zcx_order_base CREATE PUBLIC. " ...ENDCLASS.
CLASS zcx_order_invalid_state DEFINITION PUBLIC INHERITING FROM zcx_order_base CREATE PUBLIC. " ...ENDCLASS.
" Fangen nach HierarchieTRY. process_order( ).
CATCH zcx_order_not_found INTO DATA(lx_not_found). " Spezifische Behandlung
CATCH zcx_order_locked INTO DATA(lx_locked). " Spezifische Behandlung
CATCH zcx_order_base INTO DATA(lx_order). " Alle anderen Order-Exceptions
CATCH cx_static_check INTO DATA(lx_other). " Alle anderen statischen Exceptions
ENDTRY.10. MESSAGE-Integration
" Nachrichtenklasse ZMSG mit Nachricht 001:" "Feld & enthält ungültigen Wert"
CLASS zcx_with_message DEFINITION PUBLIC INHERITING FROM cx_static_check CREATE PUBLIC.
PUBLIC SECTION. INTERFACES: if_t100_message. INTERFACES: if_t100_dyn_msg.
CONSTANTS: BEGIN OF field_invalid, msgid TYPE symsgid VALUE 'ZMSG', msgno TYPE symsgno VALUE '001', attr1 TYPE scx_attrname VALUE 'MV_FIELD', attr2 TYPE scx_attrname VALUE '', attr3 TYPE scx_attrname VALUE '', attr4 TYPE scx_attrname VALUE '', END OF field_invalid.
DATA: mv_field TYPE string READ-ONLY.
METHODS: constructor IMPORTING textid LIKE if_t100_message=>t100key OPTIONAL previous LIKE previous OPTIONAL iv_field TYPE string OPTIONAL.
ENDCLASS.
" Exception werfenRAISE EXCEPTION TYPE zcx_with_message EXPORTING textid = zcx_with_message=>field_invalid iv_field = 'KUNNR'.
" Text abrufenCATCH zcx_with_message INTO DATA(lx_error). " get_text() liefert: "Feld KUNNR enthält ungültigen Wert" DATA(lv_msg) = lx_error->get_text( ).
" Oder als MESSAGE MESSAGE lx_error TYPE 'I'.11. RESUMABLE Exceptions
CLASS zcx_resumable_error DEFINITION PUBLIC INHERITING FROM cx_static_check CREATE PUBLIC.
PUBLIC SECTION. DATA: mv_value TYPE i READ-ONLY.
METHODS: constructor IMPORTING iv_value TYPE i OPTIONAL previous LIKE previous OPTIONAL.ENDCLASS.
METHOD process_values. LOOP AT lt_values INTO DATA(lv_value). IF lv_value < 0. " RESUMABLE: Kann mit RESUME fortgesetzt werden RAISE RESUMABLE EXCEPTION TYPE zcx_resumable_error EXPORTING iv_value = lv_value. ENDIF.
" Weiterverarbeitung... ENDLOOP.ENDMETHOD.
" Aufruf mit RESUMETRY. process_values( ).
CATCH BEFORE UNWIND zcx_resumable_error INTO DATA(lx_error). WRITE: / 'Warnung: Negativer Wert', lx_error->mv_value. RESUME. " Fortsetzen nach RAISE RESUMABLEENDTRY.12. Best Practice: Exception Factory
CLASS zcx_factory DEFINITION. PUBLIC SECTION. CLASS-METHODS: not_found IMPORTING iv_entity TYPE string iv_id TYPE string RETURNING VALUE(ro_exception) TYPE REF TO zcx_not_found,
validation_failed IMPORTING iv_field TYPE string iv_message TYPE string RETURNING VALUE(ro_exception) TYPE REF TO zcx_validation_error.ENDCLASS.
CLASS zcx_factory IMPLEMENTATION. METHOD not_found. ro_exception = NEW #( iv_entity = iv_entity iv_id = iv_id ). ENDMETHOD.
METHOD validation_failed. ro_exception = NEW #( iv_field = iv_field iv_message = iv_message ). ENDMETHOD.ENDCLASS.
" VerwendungRAISE EXCEPTION zcx_factory=>not_found( iv_entity = 'Customer' iv_id = '12345').Exception-Klasse in SE24 erstellen
- SE24 → Klasse anlegen (Name beginnt mit
ZCX_oderYCX_) - Superklasse:
CX_STATIC_CHECK(oder andere) - Interfaces:
IF_T100_MESSAGEhinzufügen - Attribute: Fehlerdaten definieren (z.B.
MV_ORDER_ID) - Texts: Exception-IDs mit Nachrichtenklasse verknüpfen
- Konstruktor: Parameter für Attribute definieren
Wichtige Hinweise / Best Practice
- CX_STATIC_CHECK für Business-Exceptions (müssen in RAISING deklariert werden).
- Eigene Hierarchie für fachliche Domänen (z.B.
ZCX_ORDER_BASE). - IF_T100_MESSAGE für mehrsprachige Texte mit Nachrichtenklasse.
- PREVIOUS für Exception-Ketten (Ursache nachverfolgen).
- READ-ONLY für Exception-Attribute (Immutability).
- Fangen Sie spezifische Exceptions zuerst, dann allgemeinere.
- THROW in
COND/SWITCHfür Inline-Exceptions. - RESUMABLE für Warnungen, die übersprungen werden können.
- Exception-Texte in Nachrichtenklasse für Übersetzbarkeit.
- get_text() für formatierte Fehlermeldung.