Appearance
Lesson 12 — EML and the RAP Transactional Buffer
Quick reference
Keep the EML Cheat Sheet nearby for command syntax, RAP responses, and transaction handling.
In Lesson 10, you built the Business Object structure:
text
ZRESV_###
↓
ZR_RESERVATION_###In Lesson 11, you added managed transactional behavior:
text
ZR_RESERVATION_###
↓
Managed Behavior Definition
↓
ZBP_R_RESV_###But so far, nobody has actually used that behavior.
Today that changes.
You will write an ABAP consumer that talks to the Reservation Business Object through:
text
EMLinstead of modifying:
text
ZRESV_###directly.
The architecture becomes:
text
ZCL_RESV_EML_###
↓
EML
↓
ZR_RESERVATION_###
↓
Managed RAP Behavior
↓
ZRESV_###Today is where managed RAP becomes visible at runtime.
Lesson information
| Teacher-led time | About 3 hours |
| Self-practice time | About 3 hours |
| Environment | SAP BTP ABAP Trial |
| Prerequisite | Lesson 11 |
| Business Object | ZR_RESERVATION_### |
| Consumer class | ZCL_RESV_EML_### |
| Main outcome | Consume a RAP BO with EML |
| Runtime focus | Transactional buffer + save boundary |
Replace:
text
###with your own student/group suffix.
Learning objectives
After the teacher-led part, you should be able to:
- explain what Entity Manipulation Language is;
- explain why EML is used instead of direct SQL for RAP BO consumption;
- distinguish the RAP Business Object from its persistence table;
- use
MODIFY ENTITIESto create, update, and delete BO instances; - use
%cidas a temporary create identifier; - interpret
MAPPED,FAILED, andREPORTED; - observe managed UUID numbering through
MAPPED; - use
READ ENTITIESto read BO instances; - explain the RAP transactional buffer;
- prove that an uncommitted modification is visible inside the same RAP transaction;
- use
COMMIT ENTITIESto trigger the RAP save sequence; - use
ROLLBACK ENTITIESto discard buffered changes; - distinguish interaction-phase responses from save-phase responses;
- verify committed state with EML and CDS Data Preview;
- explain why
COMMIT WORKis not the teaching focus for RAP consumers; - explain why
IN LOCAL MODEis not used in this external consumer class.
1. What is EML?
EML stands for:
text
Entity Manipulation LanguageIt is the ABAP language syntax used to work with RAP Business Objects.
With EML, ABAP code can request operations such as:
text
READ
CREATE
UPDATE
DELETE
EXECUTE ACTIONagainst the Business Object contract.
For example:
text
ABAP consumer
↓
READ ENTITIES / MODIFY ENTITIES
↓
RAP Business Object2. Why not just use ABAP SQL?
You already know how to write:
abap
INSERT zresv_### ...
UPDATE zresv_### ...
DELETE FROM zresv_### ...Those statements modify persistence directly.
Once the Reservation is owned by a RAP Business Object, normal application consumers should work through:
text
RAP behaviorbecause direct table writes can bypass current or future behavior such as:
text
validations
determinations
authorization
actions
feature control
messages
locking expectations
transactional buffering
save sequencingThe key transition is:
text
Before RAP
ABAP code → ABAP SQL → table
With RAP
ABAP code → EML → RAP BO → persistence3. EML does not mean OData
EML is an ABAP consumption mechanism.
It does not require:
text
Service Definition
Service Binding
OData
Fiori ElementsToday the consumer is another ABAP class in the same ABAP system.
Later an OData consumer can reach the same Business Object through a business service.
4. The RAP transaction
A RAP transaction moves BO data from one consistent state to another.
A simplified model is:
text
EML request
↓
interaction phase
↓
RAP transactional buffer
↓
COMMIT ENTITIES
↓
save sequence
↓
persistenceThe important new concept is:
text
transactional buffer5. What is the transactional buffer?
When an EML consumer creates or changes a BO instance, the change participates in the RAP transaction.
Do not imagine every EML modification as:
text
one immediate SQL statementInstead, think:
text
requested BO change
↓
transactional buffer
↓
later save boundaryThis lets multiple BO operations belong to one logical transaction.
6. What ends the RAP transaction?
In an external EML test/consumer scenario, the important statements are:
text
COMMIT ENTITIES
ROLLBACK ENTITIESCOMMIT ENTITIES
Triggers the RAP save sequence for all BOs changed in the current transaction. A successful save persists the buffered work together. If an on-save validation fails, the transaction is not partially committed.
ROLLBACK ENTITIES
Discards the current RAP transactional changes and resets the transactional buffer.
7. Why not teach COMMIT WORK here?
RAP integrates with the ABAP LUW, and COMMIT WORK can implicitly trigger RAP saving.
However, this course teaches the RAP-specific transaction boundary explicitly:
abap
COMMIT ENTITIESThat makes the BO save sequence visible and lets the consumer retrieve RAP save responses.
For RAP learning and certification work, this is the clearer model.
8. The class we will create
Create:
text
ZCL_RESV_EML_###Implement:
text
IF_OO_ADT_CLASSRUNArchitecture:
text
ZCL_RESV_EML_###
↓
EML
↓
Reservation BOThis class is a BO consumer.
It is not a Behavior Pool.
9. Create the consumer class
Right-click your package.
Choose:
text
New
→ ABAP ClassUse:
text
Name: ZCL_RESV_EML_###
Description: Reservation EML ConsumerAdd:
abap
INTERFACES if_oo_adt_classrun.Activate the empty class before continuing.
10. The lifecycle we will prove
The class will demonstrate:
text
CREATE two Reservations
↓
READ before COMMIT
↓
COMMIT
↓
READ persisted state
↓
UPDATE one Reservation
↓
READ changed buffer state
↓
ROLLBACK
↓
prove old value remains
↓
UPDATE again
↓
COMMIT
↓
DELETE second Reservation
↓
COMMIT
↓
final READ + F8 previewThis gives you a complete RAP consumer lifecycle.
11. Why create two Reservations?
We want the final state to be visible after the class finishes.
So the demonstration uses:
text
Reservation A
→ survives
→ gets updated
Reservation B
→ gets deleted through EMLAfter the final run, CDS Data Preview should show Reservation A but not Reservation B.
12. Use two reserved training numbers
Use:
text
92000001
92000002These values belong only to this Lesson 12 demonstration.
The Companion's repeatable cleanup pattern lets you rerun the class several times.
During the normal first guided run, create them only once.
13. First MODIFY ENTITIES
Start with a create request:
abap
MODIFY ENTITIES OF ZR_Reservation_###
ENTITY Reservation
CREATE FIELDS (
ReservationNo
CustomerName
CustomerEmail
WorkshopCode
ReservationDate
Status
Fee
CurrencyCode
Notes )
WITH VALUE #(
( %cid = 'RES_A'
ReservationNo = '92000001'
CustomerName = 'EML Student A'
CustomerEmail = 'student.a@example.com'
WorkshopCode = 'RAP101'
ReservationDate = cl_abap_context_info=>get_system_date( )
Status = 'N'
Fee = '150.00'
CurrencyCode = 'EUR'
Notes = 'Created with EML' )
( %cid = 'RES_B'
ReservationNo = '92000002'
CustomerName = 'EML Student B'
CustomerEmail = 'student.b@example.com'
WorkshopCode = 'RAP101'
ReservationDate = cl_abap_context_info=>get_system_date( )
Status = 'N'
Fee = '175.00'
CurrencyCode = 'EUR'
Notes = 'Delete demonstration' ) )
MAPPED DATA(mapped_create)
FAILED DATA(failed_create)
REPORTED DATA(reported_create).Replace ###.
14. Why is Uuid missing from CREATE FIELDS?
Lesson 11 declared:
abap
field ( numbering : managed, readonly ) Uuid;Therefore the consumer does not provide the UUID.
RAP generates it.
This is managed early numbering.
15. What is %cid?
The create request uses:
text
%cidThink of %cid as:
text
consumer-side temporary identifierBefore the consumer knows the new technical key, it can refer to the requested create instance using %cid.
Example:
text
RES_Ais not stored as the Reservation UUID.
It is only the create request's correlation identifier.
16. Inspect MAPPED
The create request returns:
text
MAPPEDFor managed early numbering, RAP can return the generated key immediately.
Conceptually:
text
%cid = RES_A
↓
RAP generates UUID
↓
MAPPED connects RES_A to that UUIDOutput:
abap
out->write( mapped_create ).Look for:
text
RES_A
RES_B
generated UUIDs17. Extract the generated UUIDs
After a successful create request:
abap
IF failed_create IS NOT INITIAL
OR NOT line_exists( mapped_create-reservation[ %cid = 'RES_A' ] )
OR NOT line_exists( mapped_create-reservation[ %cid = 'RES_B' ] ).
out->write( failed_create ).
out->write( reported_create ).
ROLLBACK ENTITIES.
RETURN.
ENDIF.
DATA(uuid_a) = mapped_create-reservation[ %cid = 'RES_A' ]-Uuid.
DATA(uuid_b) = mapped_create-reservation[ %cid = 'RES_B' ]-Uuid.Use %cid to correlate each response with its create request; the response table order is not the business identity.
The complete Companion consumer applies this guard throughout the lifecycle.
For the guided exercise, first inspect mapped_create so you understand its shape.
18. Inspect FAILED
The create request also returns:
text
FAILEDFAILED identifies entity instances for which the requested operation failed.
Output:
abap
out->write( failed_create ).For today's valid creates, expect it to be empty.
Later validations and authorization can cause meaningful failures.
19. Inspect REPORTED
The third response is:
text
REPORTEDIt carries RAP-reported information/messages associated with BO processing.
Output:
abap
out->write( reported_create ).Today it will normally be empty because you have not implemented business messages yet.
Lesson 13 will make REPORTED much more interesting.
20. Stop if CREATE failed
Do not continue blindly if the create operation failed.
Use a guard such as:
abap
IF failed_create IS NOT INITIAL.
out->write( 'Create failed. Stop the lifecycle.' ).
ROLLBACK ENTITIES.
RETURN.
ENDIF.This protects the later steps from assuming that mapped keys exist.
21. Read the new instance before commit
Now use EML to read Reservation A before COMMIT ENTITIES:
abap
READ ENTITIES OF ZR_Reservation_###
ENTITY Reservation
FIELDS (
Uuid
ReservationNo
CustomerName
Status
Fee
CurrencyCode
Notes )
WITH VALUE #( ( Uuid = uuid_a ) )
RESULT DATA(buffer_read)
FAILED DATA(failed_buffer_read).Output:
abap
out->write( buffer_read ).22. What does the pre-commit READ prove?
The instance can already be read inside the same RAP transaction.
That demonstrates:
text
CREATE request
↓
transactional buffer
↓
READ sees buffered stateThe point is not that the table was already permanently saved.
The point is that the RAP transaction has a working state before persistence.
23. Commit the two creates
Now persist the buffered work:
abap
COMMIT ENTITIES
RESPONSE OF ZR_Reservation_###
FAILED DATA(failed_commit_create)
REPORTED DATA(reported_commit_create).
DATA(create_commit_subrc) = sy-subrc.Then inspect:
abap
out->write( failed_commit_create ).
out->write( reported_commit_create ).For today's simple BO, expect a successful commit.
24. Why can COMMIT have its own responses?
The interaction request and the save sequence are not the same phase.
You already inspected responses from:
text
MODIFY ENTITIESNow the long form of:
text
COMMIT ENTITIES RESPONSE OF ...can return save-phase:
text
FAILED
REPORTEDThis distinction becomes essential when Lesson 13 introduces validations during save processing.
25. Check sy-subrc after COMMIT
Capture sy-subrc immediately after:
abap
COMMIT ENTITIESas shown above, before output or further operations. Check the captured value:
abap
IF create_commit_subrc = 0 AND failed_commit_create IS INITIAL.
out->write( 'Create commit succeeded.' ).
ELSE.
out->write( 'Create commit failed.' ).
ROLLBACK ENTITIES.
RETURN.
ENDIF.An on-save validation failure produces sy-subrc = 4; Lesson 13 demonstrates this. Stop and roll back after a failed save in these consumer exercises.
Do not use sy-dbcnt as the RAP commit success indicator.
26. Read after commit
Read Reservation A again:
abap
READ ENTITIES OF ZR_Reservation_###
ENTITY Reservation
ALL FIELDS
WITH VALUE #( ( Uuid = uuid_a ) )
RESULT DATA(after_create_commit)
FAILED DATA(failed_after_create_commit).Output the result.
Now inspect administrative fields such as:
text
LocalCreatedBy
LocalCreatedAt
LocalLastChangedBy
LocalLastChangedAt
LastChangedAtYou are now seeing the managed BO state after persistence.
27. First UPDATE — do not commit it
Change Reservation A from:
text
Nto:
text
Cusing EML:
abap
MODIFY ENTITIES OF ZR_Reservation_###
ENTITY Reservation
UPDATE FIELDS ( Status Notes )
WITH VALUE #(
( Uuid = uuid_a
Status = 'C'
Notes = 'Buffered change - will rollback' ) )
FAILED DATA(failed_update_rollback)
REPORTED DATA(reported_update_rollback).
IF failed_update_rollback IS NOT INITIAL.
out->write( failed_update_rollback ).
out->write( reported_update_rollback ).
ROLLBACK ENTITIES.
RETURN.
ENDIF.Do not commit yet.
28. Read the changed buffer state
Immediately read Reservation A:
abap
READ ENTITIES OF ZR_Reservation_###
ENTITY Reservation
FIELDS ( Uuid ReservationNo Status Notes )
WITH VALUE #( ( Uuid = uuid_a ) )
RESULT DATA(before_rollback)
FAILED DATA(failed_before_rollback).Expected inside the transaction:
text
Status = C
Notes = Buffered change - will rollback29. Roll back the UPDATE
Now execute:
abap
ROLLBACK ENTITIES.This discards the current RAP transactional changes since the last commit and resets the buffer.
30. Read after rollback
Read Reservation A again:
abap
READ ENTITIES OF ZR_Reservation_###
ENTITY Reservation
FIELDS ( Uuid ReservationNo Status Notes )
WITH VALUE #( ( Uuid = uuid_a ) )
RESULT DATA(after_rollback)
FAILED DATA(failed_after_rollback).Expected:
text
Status = N
Notes = Created with EMLThis is one of the most important observations in the lesson.
31. What rollback proved
You observed:
text
Persistent state = N
EML UPDATE
↓
Buffer state = C
ROLLBACK ENTITIES
↓
Buffer discarded
↓
Persistent state still = NThat is the RAP transactional buffer in action.
32. UPDATE again and keep it
Perform the update again, this time with a note that should persist:
abap
MODIFY ENTITIES OF ZR_Reservation_###
ENTITY Reservation
UPDATE FIELDS ( Status Notes )
WITH VALUE #(
( Uuid = uuid_a
Status = 'C'
Notes = 'Confirmed through EML' ) )
FAILED DATA(failed_update_commit)
REPORTED DATA(reported_update_commit).
IF failed_update_commit IS NOT INITIAL.
out->write( failed_update_commit ).
out->write( reported_update_commit ).
ROLLBACK ENTITIES.
RETURN.
ENDIF.If the update succeeds, commit it:
abap
COMMIT ENTITIES
RESPONSE OF ZR_Reservation_###
FAILED DATA(failed_commit_update)
REPORTED DATA(reported_commit_update).
DATA(update_commit_subrc) = sy-subrc.
IF update_commit_subrc <> 0 OR failed_commit_update IS NOT INITIAL.
out->write( failed_commit_update ).
out->write( reported_commit_update ).
ROLLBACK ENTITIES.
RETURN.
ENDIF.33. Verify the committed UPDATE
Read Reservation A again.
Expected:
text
ReservationNo = 92000001
Status = C
Notes = Confirmed through EMLAlso compare:
text
LocalCreatedAt
LocalLastChangedAtThe last-change information should reflect the managed lifecycle.
34. DELETE Reservation B through EML
Now delete only Reservation B:
abap
MODIFY ENTITIES OF ZR_Reservation_###
ENTITY Reservation
DELETE FROM VALUE #( ( Uuid = uuid_b ) )
FAILED DATA(failed_delete)
REPORTED DATA(reported_delete).
IF failed_delete IS NOT INITIAL.
out->write( failed_delete ).
out->write( reported_delete ).
ROLLBACK ENTITIES.
RETURN.
ENDIF.Do not use:
abap
DELETE FROM zresv_###The delete request goes through the BO contract.
35. Commit the DELETE
Persist the delete:
abap
COMMIT ENTITIES
RESPONSE OF ZR_Reservation_###
FAILED DATA(failed_commit_delete)
REPORTED DATA(reported_commit_delete).
DATA(delete_commit_subrc) = sy-subrc.
IF delete_commit_subrc <> 0 OR failed_commit_delete IS NOT INITIAL.
out->write( failed_commit_delete ).
out->write( reported_commit_delete ).
ROLLBACK ENTITIES.
RETURN.
ENDIF.Check:
text
FAILED
REPORTED
sy-subrc36. Prove Reservation B is gone
Try to read it through EML:
abap
READ ENTITIES OF ZR_Reservation_###
ENTITY Reservation
FIELDS ( Uuid ReservationNo Status )
WITH VALUE #( ( Uuid = uuid_b ) )
RESULT DATA(deleted_read)
FAILED DATA(failed_deleted_read).The result should contain no successful Reservation B instance.
Inspect the failed read response as part of the exercise.
37. Final read of Reservation A
Read the surviving Reservation A:
abap
READ ENTITIES OF ZR_Reservation_###
ENTITY Reservation
ALL FIELDS
WITH VALUE #( ( Uuid = uuid_a ) )
RESULT DATA(final_read)
FAILED DATA(failed_final_read).Output it.
Expected business state:
text
ReservationNo = 92000001
Status = C
Notes = Confirmed through EML38. Run the class
Activate:
text
Ctrl+F3Run:
text
F9Your output should demonstrate these phases:
text
CREATE responses
pre-commit READ
create COMMIT responses
post-commit READ
buffered UPDATE
ROLLBACK proof
committed UPDATE
DELETE responses
final READDo not focus only on the final row.
The important learning is the state transition through the RAP transaction.
39. Preview the final state with F8
After the class finishes, open:
text
ZR_RESERVATION_###Press:
text
F8Filter or find:
text
ReservationNo = 92000001Expected:
text
92000001 exists
Status = CThen check:
text
92000002Expected:
text
not presentThis is the visible payoff of the lesson.
40. Compare EML output with Data Preview
The class output shows the RAP interaction and transaction responses.
CDS Data Preview shows the final persisted state.
Use both views:
text
F9 class output
=
what happened during the RAP transaction
F8 CDS Data Preview
=
what remains persisted afterward41. MAPPED, FAILED, REPORTED summary
MAPPED
Most important for operations where preliminary identifiers must be connected to final identifiers.
Today:
text
%cid → managed UUIDFAILED
Identifies requested entity operations that failed.
REPORTED
Carries reported RAP information/messages.
Today it is mostly quiet.
Lesson 13 will deliberately produce business messages.
42. Why MAPPED matters for managed numbering
Before CREATE:
text
consumer knows %cidAfter managed early numbering:
text
RAP knows UUIDMAPPED connects them.
This lets the consumer continue working with the newly created instance inside the same logical flow.
43. Interaction-phase vs save-phase responses
Do not merge these mentally.
Interaction phase
Examples:
text
MODIFY ENTITIES
READ ENTITIESreturn operation responses such as:
text
FAILED
REPORTEDMODIFY ENTITIES can additionally return MAPPED for creates with generated identifiers. READ ENTITIES returns the data in RESULT, not MAPPED.
Save phase
text
COMMIT ENTITIES RESPONSE OF ...can return save-related:
text
FAILED
REPORTEDLesson 13 will make the save-phase distinction important through validations.
44. Why no IN LOCAL MODE?
You may see RAP implementation examples containing:
abap
IN LOCAL MODEDo not add it to today's external consumer class.
IN LOCAL MODE is available when a Behavior Pool accesses its own BO. It bypasses checks such as authorization and feature control for that local access. It cannot be added to an external consumer or to access another BO merely because the caller is a Behavior Pool.
Today's class is acting as a normal external ABAP consumer of the BO.
It should respect the contract.
45. EML is statically checked against the BO
One advantage of EML is that your ABAP code is checked against the RAP Business Object definition.
If you try to:
text
use a field that does not exist
request behavior that is not enabled
write a protected field in an invalid way
use the wrong entity nameADT can detect many problems during syntax checking.
Use:
text
Ctrl+F2often.
46. Useful ADT navigation
From your EML class, place the cursor on:
text
ZR_RESERVATION_###Use:
text
F3or:
text
Ctrl+clickwhere supported.
Move between:
text
consumer class
root CDS
Behavior Definition
Behavior Pool
persistence tableCertification work is faster when you can navigate the model quickly.
47. What EML does not replace
EML is how ABAP works with RAP BOs.
It does not replace:
text
CDS modeling
Behavior Definitions
business services
OData
Fiori ElementsInstead, it is one consumer path.
Architecture:
text
ABAP consumer → EML ┐
│
OData consumer ─────┼→ RAP Business Object
│
future UI ──────────┘48. Do not use direct persistence writes as a shortcut
After creating the EML class, do not mix in:
abap
UPDATE zresv_###
DELETE FROM zresv_###just because those statements are familiar.
The point of this lesson is to observe the BO contract and transaction.
A read-only SQL query used only for controlled training setup is different from bypassing RAP for writes.
All business modifications in this lesson must go through EML.
49. Repeatability note
If you rerun the guided class, the reserved business numbers may already exist from an earlier run.
The Companion's repeatable reset pattern:
text
reads existing training UUIDs
↓
deletes them through EML
↓
commits the cleanupDo not solve repeatability with direct table DELETE.
50. Knowledge check
1. What does EML stand for?
2. Why should a RAP consumer use EML instead of direct table writes?
3. What is %cid?
4. What does MAPPED tell a consumer?
5. What does FAILED contain?
6. What is REPORTED for?
7. When is the managed UUID available in this scenario?
8. What is the RAP transactional buffer?
9. Why can READ ENTITIES see a buffered change before commit?
10. What does COMMIT ENTITIES trigger?
11. What does ROLLBACK ENTITIES do?
12. What did the rollback experiment prove?
13. Why can COMMIT have separate FAILED and REPORTED responses?
14. Why is sy-dbcnt not the main RAP save result?
15. Why is IN LOCAL MODE not used in this consumer class?
16. How can you visually verify the final persistent state after running the class?
51. Teacher-led completion checklist
52. End-of-day mental model
You can now draw the first complete executable RAP path in the course:
text
ZCL_RESV_EML_###
↓
READ / MODIFY ENTITIES
↓
RAP transactional buffer
↓
COMMIT ENTITIES
↓
Managed RAP save sequence
↓
ZRESV_###The progression so far is:
text
Lesson 10
What does the BO look like?
Lesson 11
What can the BO do?
Lesson 12
How does ABAP consume it and control the transaction?Next, the BO needs real business rules.
Lesson 13 introduces:
text
validations
determinations
RAP messages