Что умеет Try Catch в ABAP?

У данной конструкции открываются дополнительные возможности, если добавить такие операторы, как CLEANUP, RESUME, RETRY и BEFORE UNWIND. Обсудим для чего они нужны и как с ними работать.

CLEANUP

Блок CLEANUP будет использоваться для удаления ссылок перед завершением вызова метода. Всякий раз, когда возникает исключение, система прекращает обработку с этого момента и переходит к соответствующему блоку TRY. Из-за такого поведения объект будет находиться в промежуточном состоянии. Блок CLEANUP предоставляет нам возможность восстановить состояние объекта перед выходом из текущего блока обработки.

CLASS lcl_main DEFINITION FINAL. PUBLIC SECTION. METHODS: method1, method2. ENDCLASS. CLASS lcl_main IMPLEMENTATION. METHOD method1. TRY. method2( ). CATCH cx_root. cl_demo_output=>write( 'catching cx_root' ). ENDTRY. cl_demo_output=>display( ). ENDMETHOD. METHOD method2. TRY. DATA(result) = 2 / 0. CLEANUP. cl_demo_output=>write( 'cleanup' ). ENDTRY. ENDMETHOD. ENDCLASS. START-OF-SELECTION. NEW lcl_main( )->method1( ).

В этом примере у нас есть «внешний» блок TRY..ENDTRY в методе method1. У нас есть «внутренний» блок TRY..ENDTRY в методе method2, который запускается из внешнего блока. Поскольку мы делим на 0, оператор деления вызовет исключение типа CX_SY_ZERODIVIDE. Поскольку мы перехватываем все исключения, используя CATCH CX_ROOT во внешнем блоке TRY, система выполнит логику блока CLEANUP во внутреннем блоке TRY.

Замените этот CX_ROOT на CX_SY_BUFFER_OVERFLOW, который не является частью иерархии CX_SY_ZERODIVIDE, поэтому CLEANUP не будет выполнена, и это приведет к дампу во время выполнения.

RESUME

С RESUME вы выходите из блока CATCH и возобновляете обработку после инструкции, вызвавшей исключение. Пример ниже отлично демонстрирует возможности RESUME.

CLASS lcl_employee DEFINITION FINAL CREATE PUBLIC. PUBLIC SECTION. TYPES: BEGIN OF empl_data, empid TYPE int4, " Employee ID emptyp TYPE string, " Org Assignment data salary TYPE decfloat16, " Pay data phone TYPE numc10, " Communication data END OF empl_data, empl_data_t TYPE SORTED TABLE OF empl_data WITH UNIQUE KEY empid. METHODS constructor IMPORTING VALUE(i_empid) TYPE int4. METHODS get_data RETURNING VALUE(rs_result) TYPE empl_data RAISING RESUMABLE(cx_no_data_found). PRIVATE SECTION. DATA emp_id TYPE int4. METHODS get_emptyp RETURNING VALUE(r_result) TYPE string RAISING cx_no_data_found. METHODS get_salary RETURNING VALUE(r_result) TYPE decfloat16 RAISING RESUMABLE(cx_no_data_found). METHODS get_phone RETURNING VALUE(r_result) TYPE numc10. METHODS get_emp_id RETURNING VALUE(r_result) TYPE int4. ENDCLASS. CLASS lcl_employee IMPLEMENTATION. METHOD constructor. me->emp_id = i_empid. ENDMETHOD. METHOD get_data. rs_result = VALUE #( empid = me->get_emp_id( ) emptyp = me->get_emptyp( ) salary = me->get_salary( ) phone = me->get_phone( ) ). ENDMETHOD. METHOD get_emptyp. r_result = SWITCH #( me->get_emp_id( ) WHEN 1 THEN |Full-Time| WHEN 2 THEN |Part-Time| WHEN 3 THEN |Contractor| WHEN 4 THEN |Casual| ELSE THROW cx_no_data_found( rel_proc_id = CONV #( me->get_emp_id( ) ) ) ). ENDMETHOD. METHOD get_phone. r_result = SWITCH #( me->get_emptyp( ) WHEN `Full-Time` THEN |1234567890| WHEN `Part-Time` THEN |5678901234| WHEN `Casual` THEN |7890123456| ELSE |0399999999| ). ENDMETHOD. METHOD get_salary. r_result = SWITCH #( me->get_emptyp( ) WHEN `Full-Time` THEN 50000 WHEN `Part-Time` THEN 25000 WHEN `Casual` THEN 5000 ELSE THROW RESUMABLE cx_no_data_found( rel_proc_id = CONV #( me->get_emp_id( ) ) ) ). ENDMETHOD. METHOD get_emp_id. r_result = me->emp_id. ENDMETHOD. ENDCLASS. DATA extract_t TYPE lcl_employee=>empl_data_t. DATA error_t TYPE string_table. START-OF-SELECTION. DATA(all_employees_t) = VALUE int4_table( ( 1 ) ( 2 ) ( 3 ) ( 4 ) ( 5 ) ). LOOP AT all_employees_t REFERENCE INTO DATA(dref). TRY. INSERT NEW lcl_employee( dref->* )->get_data( ) INTO TABLE extract_t. CATCH BEFORE UNWIND cx_no_data_found INTO DATA(no_data_error). IF no_data_error->is_resumable = abap_true. " Вызванное возобновляемое исключение RESUME. CLEANUP INTO DATA(lv_clear). ELSE. " Вызванное не возобновляемое исключение error_t = VALUE #( BASE error_t ( no_data_error->get_text( ) ) ). ENDIF. ENDTRY. ENDLOOP. cl_demo_output=>new( )->write( extract_t )->write( error_t )->display( ).

Благодаря RESUME в методе получения зарплаты, когда зарплата не была найдена, в таком случае нам надо сообщить об ошибке и добавить запись. Если убрать RESUME то после вызова исключения, запись в таблицу добавленна не будет.

Не обращайте внимание на BEFORE UNWIND, о нём мы поговорим чуть позже.

RETRY

С RETRY вы выходите из блока CATCH и прыгаете обратно в блок TRY структура управления для того, чтобы повторить полный блок TRY. Конечно, вы должны позаботиться о том, чтобы исключение не повторялось снова и снова, иначе вы попадете в бесконечный цикл.

CLASS lcl_main DEFINITION FINAL. PUBLIC SECTION. METHODS: method1, method2. ENDCLASS. CLASS lcl_main IMPLEMENTATION. METHOD method1. TRY. method2( ). CATCH BEFORE UNWIND cx_root. cl_demo_output=>write( 'catching cx_root' ). ENDTRY. cl_demo_output=>display( ). ENDMETHOD. METHOD method2. DATA(index) = 0. TRY. DATA(result) = 2 / index. CATCH cx_sy_zerodivide. cl_demo_output=>write( 'catching cx_sy_zerodivide' ). index = 1. cl_demo_output=>write( 'catching retry' ). RETRY. ENDTRY. ENDMETHOD. ENDCLASS. START-OF-SELECTION. NEW lcl_main( )->method1( ).

BEFORE UNWIND

Если указано дополнение BEFORE UNWIND, контекст, в котором исключение был вызван, включая все вызванные процедуры и их локальные данные, удаляется только после выхода из CATCH блок. Если добавление не указано, контекст удаляется до выполнения CATCH блока.

  • Если дополнение BEFORE UNWIND указано, оператор RESUME может быть использован в CATCH блоке для обработки возобновляемого исключения, чтобы возобновить обработку после инструкции, которая вызвала исключение. Это единственный случай, когда контекст исключения не удаляется при выходе из CATCH блока.
  • Возобновляемые исключения также могут обрабатываться CATCH блоками без добавления BEFORE UNWIND. В этом случае контекст исключения удаляется перед процессом обработки, и оператор RESUME не может быть указан.
  • Любые CLEANUP блоки всегда выполняется непосредственно перед удалением их контекста. Если BEFORE UNWIND используется, то после обработки исключений и во всех остальных случаях до обработки исключений.
  • Использование дополнения BEFORE UNWIND для CATCH требуется только тогда, когда заявление RESUME используется. Однако это в принципе разрешено во время обработки исключений, если контекст исключения должен быть оценен перед любыми действиями по очистке в CLEANUP блоки. Это имеет смысл, например, при обработке узких мест в ресурсах, если высвобождение ресурсов в CLEANUP блоке изменили бы контекст и, таким образом, произвели бы вычисление, свободные ресурсы в обработчике исключений бессмысленны.
Начать дискуссию