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, тощо).
BDEF derived types
Section titled “BDEF derived types”RAP передає дані через спеціально сформовані типи. Кілька ключових компонентів, які зустрінеш у кожному EML-виклику:
| Компонент | Призначення |
|---|---|
%cid | Content ID — тимчасовий ідентифікатор ще не збереженого instance (потрібен, щоб звʼязати create-by-association з parent) |
%cid_ref | Посилання на %cid parent-instance у межах того самого запиту (для deep create) |
%key | Група полів первинного ключа |
%pky | Parent key — ключ parent entity (для children у composition) |
%control | Прапорці, які поля задіяні у запиті (важливо для partial update) |
%is_draft | Чи це draft-instance (00 — active, 01 — draft) |
%target | Адресує child entity у create-by-association |
%tky | Transactional key — %key + %is_draft (одним махом) |
%pid | Preliminary ID у сценарії late numbering — коли реальний ключ присвоюється у save-фазі |
%control — часта причина багів: якщо не виставити прапорець для поля, RAP думає, що ти це поле не хочеш змінювати, навіть якщо воно в структурі заповнене.
MODIFY — create / update / delete / execute
Section titled “MODIFY — create / update / delete / execute”Один оператор MODIFY ENTITIES агрегує всі модифікаційні операції:
" Створення одного root instanceMODIFY 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 — отримати дані
Section titled “READ — отримати дані”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).COMMIT / ROLLBACK ENTITIES
Section titled “COMMIT / ROLLBACK ENTITIES”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 OF — COMMIT ENTITIES повертає sy-subrc у короткій формі, а COMMIT ENTITIES RESPONSES повертає списки успішних/невдалих операцій для всіх BO у LUW.
GET PERMISSIONS
Section titled “GET PERMISSIONS”Перевірити, які операції на конкретному 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 та AUTO FILL CID
Section titled “AUGMENTING та AUTO FILL CID”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 method у ABP
Section titled “Handler method у ABP”Метод 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.
FAILED, MAPPED, REPORTED
Section titled “FAILED, MAPPED, REPORTED”Три стандартні поверні-таблиці майже у кожній 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.
Посилання
Section titled “Посилання”Адаптовано з 08_EML_ABAP_for_RAP.md (Apache 2.0).