Appearance
Lesson 9 — Working with Custom Persistence
In Lesson 8, you designed and activated:
text
ZRESV_###The table exists.
But it contains:
text
0 rowsToday you will manipulate that persistence directly from ABAP.
You will learn:
text
table as ABAP type
UUID generation
INSERT
SELECT
UPDATE
DELETE
sy-dbcnt
safe WHERE conditions
implicit client handling
verification
cleanupThis is intentionally the last lesson where direct writes to this table are presented as the main learning path.
Later, once the table belongs to a RAP business object, consumers should use the RAP contract instead.
Lesson information
| Teacher-led time | About 3 hours |
| Self-practice time | About 3 hours |
| Environment | SAP BTP ABAP Trial |
| Prerequisites | Lesson 8 |
| Existing table | ZRESV_### |
| Guided class | ZCL_RESV_SQL_### |
| Main outcome | Safe full DML lifecycle |
Learning objectives
After the teacher-led part, you should be able to:
- use a Database Table as an ABAP structured type;
- generate an X16 UUID with
CL_SYSTEM_UUID; - handle
CX_UUID_ERROR; - construct a table-row value with
VALUE; - create a row with
INSERT; - verify a write using
SELECT SINGLE; - interpret
sy-dbcnt; - update a specific row safely;
- explain why an unrestricted
UPDATEis dangerous; - delete only the row owned by the current exercise;
- verify cleanup;
- explain implicit client handling during normal ABAP SQL access;
- explain why direct SQL does not populate RAP administrative fields automatically;
- distinguish low-level persistence mechanics from the future RAP application API.
Certification alignment
This lesson reinforces current ABAP Cloud backend skills around:
text
ABAP SQL
database write access
structured Dictionary types
exception handling
system fields
client-dependent access
safe data modification
transaction awareness
transition to Business Objects / EMLSAP's current core ABAP learning path also connects database work with Business Object and EML-based updates.
The course teaches direct writes here for persistence literacy, then moves to RAP.
1. Architecture today
Lesson 8 ended here:
text
ZRESV_###Today:
text
ZCL_RESV_SQL_###
↓
ABAP SQL
↓
ZRESV_###Later:
text
Consumer
↓
EML / RAP service
↓
RAP behavior
↓
ZRESV_###Understand the lower layer before replacing it with a business-object API.
2. A Database Table is also an ABAP type
The table definition can type an ABAP structure:
abap
DATA reservation TYPE zresv_###.That structure contains the table row fields.
You can also define:
abap
DATA reservations TYPE STANDARD TABLE OF zresv_###
WITH EMPTY KEY.So the Dictionary object is both:
text
database schema
+
global ABAP type3. Create the SQL exercise class
Create:
text
ZCL_RESV_SQL_###Implement:
text
IF_OO_ADT_CLASSRUNThe class will perform this lifecycle:
text
Generate UUID
↓
INSERT
↓
SELECT / verify
↓
UPDATE
↓
SELECT / verify
↓
DELETE
↓
verify cleanup4. Why generate the UUID ourselves today?
The table key contains:
text
uuid : SYSUUID_X16Today there is no RAP managed numbering yet.
So low-level ABAP code must supply the UUID.
Use:
abap
cl_system_uuid=>create_uuid_x16_static( )5. UUID generation can fail
The method can raise:
text
CX_UUID_ERRORUse:
abap
TRY.
DATA(uuid) = cl_system_uuid=>create_uuid_x16_static( ).
CATCH cx_uuid_error INTO DATA(uuid_error).
out->write( uuid_error->get_text( ) ).
RETURN.
ENDTRY.Do not assume infrastructure calls can never fail.
6. Construct a Reservation row
Use the table as a structured type:
abap
DATA(reservation) = VALUE zresv_###(
uuid = uuid
reservation_no = '00000001'
customer_name = 'Ada Martin'
customer_email = 'ada@example.com'
workshop_code = 'ABAP01'
reservation_date = sy-datum
status = 'N'
fee = '125.00'
currency_code = 'EUR'
notes = 'Lesson 9 SQL exercise'
).Notice what is not supplied:
text
client
RAP administrative fields7. Why don't we fill client?
The table is client-dependent.
Normal ABAP SQL performs implicit client handling.
So application code does not normally set:
text
clientmanually in this exercise.
Likewise, ordinary reads do not need:
abap
WHERE client = @sy-mandtClient handling is supplied by the ABAP SQL layer.
8. Why are RAP admin fields left initial?
These fields exist:
text
local_created_by
local_created_at
local_last_changed_by
local_last_changed_at
last_changed_atBut today's direct SQL code does not populate them.
That is intentional.
Direct table writes do not magically execute future managed RAP behavior.
This difference becomes visible later when RAP manages the persistence.
9. INSERT
Create the row:
abap
INSERT zresv_### FROM @reservation.Read it as:
Insert this ABAP structure as a row in the current client's
ZRESV_###table.
10. Capture sy-dbcnt immediately
After a database-changing statement:
abap
sy-dbcntcontains the number of rows processed.
Copy it immediately:
abap
DATA(inserted_rows) = sy-dbcnt.Then output:
abap
out->write(
|Inserted rows: { inserted_rows }|
).Expected:
text
111. Why immediately?
System fields are reused by later statements.
If you care about the result of:
text
INSERT
UPDATE
DELETEcapture it before executing unrelated operations.
This is the same discipline you learned earlier with sy-subrc.
12. Verify the INSERT
Never assume:
text
statement executed
=
data is exactly what I intendedRead the row back:
abap
SELECT SINGLE FROM zresv_###
FIELDS uuid,
reservation_no,
customer_name,
workshop_code,
status,
fee,
currency_code
WHERE uuid = @uuid
INTO @DATA(created_reservation).Then:
abap
out->write( 'After INSERT' ).
out->write( created_reservation ).13. Why use the UUID in the WHERE condition?
The UUID identifies the row created by this run.
That gives a safe, precise condition:
abap
WHERE uuid = @uuidIt avoids depending on names or other non-unique business data.
14. UPDATE
Change only the status:
abap
UPDATE zresv_###
SET status = 'C'
WHERE uuid = @uuid.Then immediately capture:
abap
DATA(updated_rows) = sy-dbcnt.Expected:
text
115. The WHERE clause is not optional thinking
This is dangerous:
abap
UPDATE zresv_###
SET status = 'C'.It means:
update every row visible to the statement.
In a shared training environment, that is unacceptable unless intentionally required.
Before executing DML, ask:
text
Which exact rows can this statement change?16. Verify the UPDATE
Read the row again:
abap
SELECT SINGLE FROM zresv_###
FIELDS uuid,
reservation_no,
customer_name,
workshop_code,
status
WHERE uuid = @uuid
INTO @DATA(updated_reservation).Expected:
text
Status = C17. DELETE
Delete only the row created by the current run:
abap
DELETE FROM zresv_###
WHERE uuid = @uuid.Capture:
abap
DATA(deleted_rows) = sy-dbcnt.Expected:
text
118. Why clean up?
A training program should be:
text
repeatable
safe
isolatedIf every run leaves random sample data, the table becomes difficult to inspect and later exercises become less deterministic.
So the class cleans up its own row.
19. Verify the DELETE
Try to read the UUID again:
abap
SELECT SINGLE FROM zresv_###
FIELDS uuid
WHERE uuid = @uuid
INTO @DATA(deleted_check).Immediately inspect:
abap
sy-subrcExpected:
text
not foundThe exact numeric value is less important than understanding that the row no longer exists.
20. Complete guided class
abap
CLASS zcl_resv_sql_### DEFINITION
PUBLIC
FINAL
CREATE PUBLIC.
PUBLIC SECTION.
INTERFACES if_oo_adt_classrun.
ENDCLASS.
CLASS zcl_resv_sql_### IMPLEMENTATION.
METHOD if_oo_adt_classrun~main.
DATA uuid TYPE sysuuid_x16.
TRY.
uuid = cl_system_uuid=>create_uuid_x16_static( ).
CATCH cx_uuid_error INTO DATA(uuid_error).
out->write( uuid_error->get_text( ) ).
RETURN.
ENDTRY.
DATA(reservation) = VALUE zresv_###(
uuid = uuid
reservation_no = '00000001'
customer_name = 'Ada Martin'
customer_email = 'ada@example.com'
workshop_code = 'ABAP01'
reservation_date = sy-datum
status = 'N'
fee = '125.00'
currency_code = 'EUR'
notes = 'Lesson 9 SQL exercise'
).
INSERT zresv_### FROM @reservation.
DATA(inserted_rows) = sy-dbcnt.
out->write(
|Inserted rows: { inserted_rows }|
).
SELECT SINGLE FROM zresv_###
FIELDS uuid,
reservation_no,
customer_name,
workshop_code,
status,
fee,
currency_code
WHERE uuid = @uuid
INTO @DATA(created_reservation).
out->write( 'After INSERT' ).
out->write( created_reservation ).
UPDATE zresv_###
SET status = 'C'
WHERE uuid = @uuid.
DATA(updated_rows) = sy-dbcnt.
out->write(
|Updated rows: { updated_rows }|
).
SELECT SINGLE FROM zresv_###
FIELDS uuid,
reservation_no,
customer_name,
workshop_code,
status
WHERE uuid = @uuid
INTO @DATA(updated_reservation).
out->write( 'After UPDATE' ).
out->write( updated_reservation ).
DELETE FROM zresv_###
WHERE uuid = @uuid.
DATA(deleted_rows) = sy-dbcnt.
out->write(
|Deleted rows: { deleted_rows }|
).
SELECT SINGLE FROM zresv_###
FIELDS uuid
WHERE uuid = @uuid
INTO @DATA(deleted_check).
IF sy-subrc <> 0.
out->write( 'Cleanup verified: row no longer exists.' ).
ELSE.
out->write( 'Cleanup check failed.' ).
ENDIF.
ENDMETHOD.
ENDCLASS.Replace all ###.
21. Run the class
Activate:
text
Ctrl + F3Run:
text
F9Expected output conceptually:
text
Inserted rows: 1
After INSERT
...
Updated rows: 1
After UPDATE
status = C
Deleted rows: 1
Cleanup verified: row no longer exists.The UUID changes every run.
22. Inspect Data Preview
Open:
text
ZRESV_###Use:
text
Alt + F8Because the class deletes its own row, the table should normally return to:
text
0 rowsThat is intentional.
23. Why no explicit COMMIT WORK exercise?
This lesson is about:
text
DML semantics
safe targeting
verification
cleanupnot about teaching manual transaction control as an application architecture.
The class performs the full lifecycle in one run and deletes its own data.
Later RAP lessons introduce the business-object transaction model and its save boundary through:
text
COMMIT ENTITIESDo not build a habit of scattering manual commits through application logic.
24. Why not teach MODIFY today?
ABAP SQL also has other write forms.
The guided lesson deliberately uses:
text
INSERT
UPDATE
DELETEbecause each operation has one clear purpose.
That makes the persistence lifecycle explicit:
text
create
change
removeThe Companion explains why MODIFY is not needed for the learning goal.
25. Direct SQL is not the future consumer API
Today you can write:
abap
UPDATE zresv_###Later the Reservation table will belong to a RAP business object.
Then direct writes could bypass:
text
validation
determination
authorization
feature control
locking / ETag expectations
RAP messages
transactional buffer
save sequenceSo this lesson teaches:
text
how persistence worksnot:
text
how a finished RAP application should expose writes26. Architectural transition
Today:
text
ZCL_RESV_SQL_###
↓
ABAP SQL
↓
ZRESV_###Soon:
text
Consumer
↓
EML
↓
RAP BO
↓
ZRESV_###That transition should now feel concrete rather than magical.
27. Common mistakes
UUID left initial
The UUID is part of the primary key.
Generate it before the INSERT.
Unsafe UPDATE
Never omit the WHERE condition accidentally.
Unsafe DELETE
Never run:
abap
DELETE FROM zresv_###.as a training cleanup shortcut.
Reading sy-dbcnt too late
Copy it immediately after the DML statement you are measuring.
Manually filling client
Normal ABAP SQL client handling supplies the current client behavior.
Expecting RAP admin fields to fill themselves
There is no managed RAP behavior yet.
Confusing SQL write success with business validity
Today's table has little business behavior.
Later RAP is where business rules become part of the approved write path.
28. Knowledge check
1. Why can ZRESV_### be used as an ABAP type?
Show answer
A Dictionary Database Table also provides the row structure as a globally usable ABAP type.
2. Why do we generate a UUID before INSERT?
Show answer
The UUID is part of the technical primary key and no RAP managed numbering exists yet.
3. Why copy sy-dbcnt immediately?
Show answer
Later statements can overwrite system fields. Capturing it immediately preserves the row count for the operation being checked.
4. Why is WHERE uuid = @uuid important?
Show answer
It targets exactly the row created by this class run and prevents broad accidental changes.
5. Why are admin fields initial in this exercise?
Show answer
Direct SQL is not managed RAP behavior and does not automatically perform the future RAP lifecycle handling.
6. Why does the class delete its own row?
Show answer
To make the exercise safe and repeatable without leaving random training data.
7. Is direct SQL the normal consumer API once RAP owns this table?
Show answer
No. Future consumers should use the RAP business-object contract, such as EML, rather than bypassing behavior with direct table writes.
29. Lesson summary
You performed a complete low-level persistence lifecycle:
text
Generate UUID
↓
INSERT
↓
SELECT
↓
UPDATE
↓
SELECT
↓
DELETE
↓
verify cleanupYou also learned the architectural boundary:
Understanding direct DML is useful. Building future application behavior around direct table writes is not the RAP architecture.
30. What comes next?
Lesson 10 starts the RAP sequence.
The first question is:
How do we turn a database table into the data model of a RAP Business Object?
You will build:
text
ZRESV_###
↓
ZR_RESERVATION_###No OData/UI is needed.
The focus moves from:
text
database rowto:
text
business object