Appearance
Lesson 9 Companion — Working with Custom Persistence
This companion expands Lesson 9 — Working with Custom Persistence.
Today's purpose is low-level persistence literacy.
The architecture is temporarily:
text
ABAP class
↓
ABAP SQL
↓
custom tableThe course will deliberately leave this architecture behind when RAP begins.
1. Database Tables as ABAP types
A Dictionary Database Table can type an ABAP structure:
abap
DATA issue TYPE zissue_###.or internal table:
abap
DATA issues TYPE STANDARD TABLE OF zissue_###
WITH EMPTY KEY.This is useful because application code does not need to redefine the row shape manually.
2. UUID generation
The guided code uses:
abap
cl_system_uuid=>create_uuid_x16_static( )which matches the SYSUUID_X16 key type.
Handle:
text
CX_UUID_ERRORexplicitly.
This same API appears in current SAP examples.
3. Why X16?
SYSUUID_X16 represents a 16-byte RAW UUID.
That format also aligns with later RAP managed UUID numbering.
Lesson 9 generates the key manually so students can see what RAP will later automate.
4. INSERT semantics
Guided statement:
abap
INSERT zresv_### FROM @reservation.It attempts to create a new row.
If a duplicate primary key is inserted without a duplicate-handling addition, a database exception can be raised.
The lesson avoids this by generating a fresh UUID for every run.
5. sy-dbcnt
For the simple guided operations:
text
INSERT one row
UPDATE one row
DELETE one rowexpected sy-dbcnt is:
text
1A value of:
text
0during a targeted update/delete usually means the condition matched no row.
That is diagnostically useful.
6. sy-subrc
The course uses sy-subrc mainly to verify the final SELECT SINGLE.
General rule:
text
check a system field immediately after the statement whose result it describesDo not execute unrelated statements first.
7. Client handling
Because the table begins with:
abap
key client : abap.clnt not null;normal ABAP SQL uses implicit client handling.
That is why the exercise does not:
text
fill client manually
add WHERE client = @sy-mandtThis behavior is separate from authorization.
8. Why admin fields remain initial
The table was designed for future RAP.
But today's write class is not RAP.
So fields such as:
text
local_created_at
local_last_changed_atdo not become meaningful automatically just because their types are RAP reuse Data Elements.
The RAP provider will later manage them when the corresponding BO semantics/behavior are in place.
9. Safe UPDATE design
Prefer a precise condition:
abap
UPDATE zresv_###
SET status = 'C'
WHERE uuid = @uuid.Before running a write statement, state in plain language:
Which rows can this modify?
If the answer is not precise, do not run it yet.
10. Safe DELETE design
Training cleanup:
abap
DELETE FROM zresv_###
WHERE uuid = @uuid.Do not use an unrestricted delete as a shortcut.
The Practice reinforces this by keeping two rows alive at the same time.
11. Why verify after writes?
A strong backend workflow is:
text
write
↓
inspect result count
↓
read back
↓
confirm intended stateThat habit is useful later with RAP responses as well.
12. Why no MODIFY statement in the guided path?
An ABAP SQL MODIFY can combine insert/update-like behavior.
That is useful in some scenarios but weakens today's conceptual separation:
text
INSERT = create
UPDATE = change
DELETE = removeThe lesson wants those operations to remain visually distinct before RAP introduces standard BO operations.
13. Why no explicit COMMIT WORK lesson?
The exercise does all changes and cleanup within one class run.
Its purpose is not manual LUW design.
Explicit transaction control deserves careful context because later RAP uses its own transaction model and:
text
COMMIT ENTITIESbecomes the meaningful BO save boundary.
The course avoids teaching a "commit after every DML" habit.
14. Guided class reference
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.15. Practice strategy: use two rows
The Practice intentionally uses two Support Issues.
Why?
Because with only one row, this statement:
abap
UPDATE zissue_###
SET status = 'I'
WHERE uuid = @uuid_a.proves that A changed.
With two rows, students can additionally prove:
text
B did not changeThat makes WHERE targeting concrete.
16. Practice reference data
Suggested Issue A:
text
IssueNo: 00000001
Title: Cannot publish service
Priority: H
Status: O
ReportedOn: current date
EstimatedCost: 75.00
Currency: EURSuggested Issue B:
text
IssueNo: 00000002
Title: Data preview mismatch
Priority: M
Status: O
ReportedOn: current date
EstimatedCost: 40.00
Currency: EUR17. Full Practice reference solution
abap
CLASS zcl_issue_sql_### DEFINITION
PUBLIC
FINAL
CREATE PUBLIC.
PUBLIC SECTION.
INTERFACES if_oo_adt_classrun.
ENDCLASS.
CLASS zcl_issue_sql_### IMPLEMENTATION.
METHOD if_oo_adt_classrun~main.
DATA uuid_a TYPE sysuuid_x16.
DATA uuid_b TYPE sysuuid_x16.
TRY.
uuid_a = cl_system_uuid=>create_uuid_x16_static( ).
uuid_b = cl_system_uuid=>create_uuid_x16_static( ).
CATCH cx_uuid_error INTO DATA(uuid_error).
out->write( uuid_error->get_text( ) ).
RETURN.
ENDTRY.
DATA(issue_a) = VALUE zissue_###(
uuid = uuid_a
issue_no = '00000001'
title = 'Cannot publish service'
requester_email = 'student@example.com'
priority = 'H'
status = 'O'
reported_on = sy-datum
estimated_cost = '75.00'
currency_code = 'EUR'
).
DATA(issue_b) = VALUE zissue_###(
uuid = uuid_b
issue_no = '00000002'
title = 'Data preview mismatch'
requester_email = 'student@example.com'
priority = 'M'
status = 'O'
reported_on = sy-datum
estimated_cost = '40.00'
currency_code = 'EUR'
).
INSERT zissue_### FROM @issue_a.
DATA(inserted_a) = sy-dbcnt.
INSERT zissue_### FROM @issue_b.
DATA(inserted_b) = sy-dbcnt.
out->write( |Inserted A: { inserted_a }| ).
out->write( |Inserted B: { inserted_b }| ).
SELECT FROM zissue_###
FIELDS uuid,
issue_no,
title,
priority,
status
WHERE uuid = @uuid_a
OR uuid = @uuid_b
ORDER BY issue_no
INTO TABLE @DATA(created_issues).
out->write( 'After INSERT' ).
out->write( created_issues ).
UPDATE zissue_###
SET status = 'I'
WHERE uuid = @uuid_a.
DATA(updated_a) = sy-dbcnt.
out->write( |Updated A: { updated_a }| ).
SELECT SINGLE FROM zissue_###
FIELDS issue_no,
status
WHERE uuid = @uuid_a
INTO @DATA(check_a).
SELECT SINGLE FROM zissue_###
FIELDS issue_no,
status
WHERE uuid = @uuid_b
INTO @DATA(check_b).
out->write( 'A after targeted UPDATE' ).
out->write( check_a ).
out->write( 'B should still be Open' ).
out->write( check_b ).
DELETE FROM zissue_###
WHERE uuid = @uuid_a.
DATA(deleted_a) = sy-dbcnt.
out->write( |Deleted A: { deleted_a }| ).
SELECT SINGLE FROM zissue_###
FIELDS issue_no,
status
WHERE uuid = @uuid_b
INTO @DATA(b_still_exists).
IF sy-subrc = 0.
out->write( 'B still exists after deleting A.' ).
out->write( b_still_exists ).
ENDIF.
DELETE FROM zissue_###
WHERE uuid = @uuid_b.
DATA(deleted_b) = sy-dbcnt.
out->write( |Cleanup B: { deleted_b }| ).
ENDMETHOD.
ENDCLASS.18. What the Practice proves
The student should be able to demonstrate:
text
two independent keys
two successful inserts
targeted update affects one row
other row remains unchanged
targeted delete removes one row
other row still exists
final cleanup removes remaining rowThat is stronger evidence of safe DML understanding than a single-row script.
19. Optional zero-row diagnostic
An advanced optional check:
- generate a UUID that was never inserted;
- run a targeted
UPDATEwith it; - inspect
sy-dbcnt.
Expected:
text
0This teaches that zero processed rows can be a meaningful diagnostic result rather than a syntax error.
20. Direct SQL vs RAP
Once the table is RAP persistence, direct application writes can bypass BO rules.
From the RAP part of the course onward:
text
direct DML
=
understanding / special infrastructure cases
EML / RAP service
=
normal BO consumer pathThat boundary should be explicit before students begin RAP.
21. Troubleshooting
CX_UUID_ERROR
Handle it and stop the run.
Do not continue with an initial key.
INSERT raises duplicate-key database error
Check:
text
fresh UUID?
same UUID assigned twice?UPDATE row count is 0
Check:
text
correct UUID variable?
row still exists?
current client?DELETE removes 0 rows
Check whether the row was already deleted.
Practice leaves data behind
Use Data Preview to identify only your rows, then write a precise cleanup condition.
Do not use unrestricted DELETE.
22. Current SAP references
Relevant current SAP topics:
text
ABAP Keyword Documentation
→ INSERT
→ UPDATE
→ DELETE
→ SELECT
→ system fields
ABAP Cloud / RAP Help
→ client handling
→ UUID examples
Basic ABAP Programming
→ database access
→ Business Objects / EML transitionThe guided APIs and write patterns are compatible with SAP BTP ABAP Trial.