Перейти до вмісту

EML — Entity Manipulation Language

EML (Entity Manipulation Language) — підмножина ABAP, спеціально призначена для роботи з даними RAP business object. Якщо ABAP SQL — це мова для таблиць бази, то EML — мова для RAP BO: ти не пишеш INSERT INTO zfoo, а кажеш «створи instance бізнес-обʼєкта ZR_Order», і фреймворк сам подбає про транзакційний буфер, блокування, авторизацію та валідації.

EML потрібен у двох місцях: у споживачах RAP BO (інший клас, фонова робота, REST handler, тест) — щоб виконати операцію; та у handler/saver methods самого behavior pool — там EML-оператори використовуються зі спеціальними доданнями типу IN LOCAL MODE.

Терміни, без яких далі не зрозуміло

Section titled “Терміни, без яких далі не зрозуміло”
  • RAP BO — дерево CDS-сутностей (root + compositions), які представляють бізнес-обʼєкт (наприклад, замовлення з позиціями).
  • BDEF (behavior definition) — файл на BDL, що декларує, які операції підтримує BO.
  • Behavior Pool (ABP) — пулл класів, що імплементують операції: handler classes (phase interaction) і saver class (persistence).
  • Transactional buffer — тимчасове сховище змін у памʼяті під час RAP LUW. COMMIT ENTITIES робить його перманентним у базі.
  • Derived types — спец-типи, які фреймворк генерує для кожного BO (%cid, %key, %control, тощо).

RAP передає дані через спеціально сформовані типи. Кілька ключових компонентів, які зустрінеш у кожному EML-виклику:

КомпонентПризначення
%cidContent ID — тимчасовий ідентифікатор ще не збереженого instance (потрібен, щоб звʼязати create-by-association з parent)
%cid_refПосилання на %cid parent-instance у межах того самого запиту (для deep create)
%keyГрупа полів первинного ключа
%pkyParent key — ключ parent entity (для children у composition)
%controlПрапорці, які поля задіяні у запиті (важливо для partial update)
%is_draftЧи це draft-instance (00 — active, 01 — draft)
%targetАдресує child entity у create-by-association
%tkyTransactional key — %key + %is_draft (одним махом)
%pidPreliminary ID у сценарії late numbering — коли реальний ключ присвоюється у save-фазі

%control — часта причина багів: якщо не виставити прапорець для поля, RAP думає, що ти це поле не хочеш змінювати, навіть якщо воно в структурі заповнене.

MODIFY — create / update / delete / execute

Section titled “MODIFY — create / update / delete / execute”

Один оператор MODIFY ENTITIES агрегує всі модифікаційні операції:

" Створення одного root instance
MODIFY ENTITIES OF zr_order
ENTITY Order
CREATE FIELDS ( OrderId Customer Total )
WITH VALUE #( ( %cid = 'ord1'
OrderId = '0001'
Customer = 'ACME'
Total = '100.00' ) )
MAPPED DATA(mapped)
FAILED DATA(failed)
REPORTED DATA(reported).

%cid = 'ord1' — це довільний string, унікальний у межах одного запиту. Потім у mapped-order можна побачити, який реальний ключ RAP присвоїв цьому %cid.

Create-by-association — коли створюємо child разом з parent:

MODIFY ENTITIES OF zr_order
ENTITY Order
CREATE FIELDS ( OrderId Customer )
WITH VALUE #( ( %cid = 'ord1' OrderId = '0001' Customer = 'ACME' ) )
CREATE BY \_Items FIELDS ( Position Material Qty )
WITH VALUE #( ( %cid_ref = 'ord1' " звʼязок з parent через cid
%target = VALUE #(
( %cid = 'it1' Position = '010' Material = 'M001' Qty = 5 )
( %cid = 'it2' Position = '020' Material = 'M002' Qty = 3 ) ) ) )
MAPPED DATA(mapped).

UPDATE — часткова модифікація:

MODIFY ENTITIES OF zr_order
ENTITY Order
UPDATE FIELDS ( Customer ) " оновлюємо тільки Customer
WITH VALUE #( ( OrderId = '0001' Customer = 'NewCo' ) )
FAILED DATA(failed)
REPORTED DATA(reported).

DELETE:

MODIFY ENTITIES OF zr_order
ENTITY Order
DELETE FROM VALUE #( ( OrderId = '0001' ) )
FAILED DATA(failed).

EXECUTE action — нестандартна операція, яку описали у BDL як action:

MODIFY ENTITIES OF zr_order
ENTITY Order
EXECUTE approve
FROM VALUE #( ( OrderId = '0001' ) )
RESULT DATA(result)
FAILED DATA(failed)
REPORTED DATA(reported).
READ ENTITIES OF zr_order
ENTITY Order
FIELDS ( Customer Total ) WITH VALUE #( ( OrderId = '0001' ) )
RESULT DATA(orders)
FAILED DATA(failed).

READ BY ASSOCIATION — іти по композиції:

READ ENTITIES OF zr_order
ENTITY Order BY \_Items
FROM VALUE #( ( OrderId = '0001' ) )
RESULT DATA(items).

Якщо хочеш витягти всі поля — замість FIELDS ( ... ) пиши ALL FIELDS WITH VALUE #( ... ):

READ ENTITIES OF zr_order
ENTITY Order
ALL FIELDS WITH VALUE #( ( OrderId = '0001' ) ( OrderId = '0002' ) )
RESULT DATA(all_orders).

EML не працює з БД одразу. Усі зміни накопичуються у transactional buffer, а запис відбувається тільки через COMMIT ENTITIES:

MODIFY ENTITIES OF zr_order ...
COMMIT ENTITIES
RESPONSE OF zr_order
MAPPED DATA(commit_mapped)
FAILED DATA(commit_failed)
REPORTED DATA(commit_reported).
IF commit_failed IS NOT INITIAL.
ROLLBACK ENTITIES. " відкат буфера
ENDIF.

Існує також коротший варіант без RESPONSE OFCOMMIT ENTITIES повертає sy-subrc у короткій формі, а COMMIT ENTITIES RESPONSES повертає списки успішних/невдалих операцій для всіх BO у LUW.

Перевірити, які операції на конкретному instance дозволені поточному користувачу, до того як пробувати їх виконати:

GET PERMISSIONS ENTITY zr_order
FROM VALUE #( ( %key-OrderId = '0001' ) )
RESULT DATA(perms).
" perms-instance[ 1 ]-%update — чи дозволено update
" perms-instance[ 1 ]-%action-approve — чи доступна action approve

Зручно для UI-логіки (приховати кнопку) і для захисту у handler-ах до виконання власне MODIFY.

  • AUGMENTING — addition до MODIFY ENTITIES всередині handler-а, яке доповнює існуючий request замість його заміни. Використовується, коли handler повинен модифікувати інші поля того самого instance без перетирання того, що передав споживач.
  • AUTO FILL CID — автогенерація %cid у CREATE, коли тобі не потрібно посилатися на нього пізніше у тому самому запиті. Економить ручне проставляння %cid = 'ord1'.
MODIFY ENTITIES OF zr_order
ENTITY Order
CREATE AUTO FILL CID
WITH VALUE #( ( OrderId = '0001'
Customer = 'ACME'
Total = '100.00' ) )
MAPPED DATA(mapped).

Метод handler-класу, що реалізує операцію, виглядає так:

METHODS update_order FOR MODIFY
IMPORTING entities FOR UPDATE Order.
METHOD update_order.
" entities — це структура з полями, які можна змінити,
" + %tky для ідентифікації instance
LOOP AT entities ASSIGNING FIELD-SYMBOL(<ent>).
" custom-логіка
ENDLOOP.
ENDMETHOD.

Інші FOR ... additions у сигнатурі handler/saver методів: FOR MODIFY, FOR READ, FOR ACTION, FOR VALIDATION, FOR DETERMINATION, FOR INSTANCE AUTHORIZATION, FOR INSTANCE FEATURES, FOR NUMBERING, FOR LOCK. Це ABAP method syntax — не плутай з BDL-тригерами (determine on modify, validation on save і т.ін.), які задаються у behavior definition.

Три стандартні поверні-таблиці майже у кожній EML-операції:

  • FAILED — instances, для яких операція не вдалася (з кодом проблеми: transition_not_allowed, not_found, auth_failed тощо).
  • MAPPED — мапінг %cid → реальний %key (для операцій create).
  • REPORTED — повідомлення (symsg, severity, прикріплені до конкретного поля/entity).

Після кожного MODIFY/READ треба їх перевіряти — інакше помилки «тихо зʼїдаються».

Приклад: повний цикл створення з перевіркою

Section titled “Приклад: повний цикл створення з перевіркою”
MODIFY ENTITIES OF zr_order
ENTITY Order
CREATE FIELDS ( OrderId Customer Total )
WITH VALUE #( ( %cid = 'new1'
OrderId = '0001'
Customer = 'ACME'
Total = '500.00' ) )
MAPPED DATA(mapped)
FAILED DATA(failed)
REPORTED DATA(reported).
IF failed-order IS NOT INITIAL.
" щось пішло не так — не комітимо
ROLLBACK ENTITIES.
RETURN.
ENDIF.
COMMIT ENTITIES
RESPONSE OF zr_order
MAPPED DATA(commit_mapped)
FAILED DATA(commit_failed)
REPORTED DATA(commit_reported).
IF commit_failed IS INITIAL.
" readable id
DATA(new_id) = mapped-order[ %cid = 'new1' ]-OrderId.
ENDIF.

Managed vs unmanaged — що це для EML

Section titled “Managed vs unmanaged — що це для EML”
  • Managed BO — фреймворк сам підтримує буфер, CUD-операції працюють out-of-the-box. create;, update;, delete; у BDL достатньо, код пишеш тільки для нестандартних речей (action, validation, determination).
  • Unmanaged BO — ти пишеш усе: handler для create, update, delete, read, + saver method для save_modified. Використовують для інтеграції з legacy-кодом, де вже є власна логіка збереження.

EML-синтаксис для споживача однаковий в обох випадках — різниця тільки в тому, хто реалізує handler methods.

Адаптовано з 08_EML_ABAP_for_RAP.md (Apache 2.0).