Appearance
Teacher Notes — Lesson 1
Student-facing files:
- Lesson 1 — Database Basics, ABAP SQL, and Code Organization
- Lesson 1 Practice — Airport Reader and Runner
This revision intentionally reinforces the students' existing OOP knowledge.
1. Architectural goal
Do not leave students with:
text
Executable Class
↓
SELECT everywhere
↓
DatabaseTeach:
text
Runner
↓
Reader
↓
ABAP SQL
↓
PersistenceThis is a teaching architecture, not the final RAP application architecture.
2. Why "Reader" and not "Service"
Do not call the data-access class Service or Business Service.
In RAP, business service has a specific meaning involving service exposure. Using "service class" here would create terminology debt.
Reader is deliberately narrow:
text
GET_ALL
GET_BY_CURRENCY
GET_BY_ID3. Why not "Repository"?
Repository can imply broader persistence responsibilities such as create, update, and delete.
Lesson 1 is read-only, so Reader is clearer.
4. Why organize code this early?
Students already know OOP. If the first database lesson puts all SQL into IF_OO_ADT_CLASSRUN~main, the course accidentally teaches them to abandon the separation they just learned.
The Runner/Reader split reinforces:
- single responsibility;
- collaboration between objects;
- method boundaries;
- typed contracts;
- keeping data-access details out of presentation/execution code.
Do not oversell this as a formal architectural pattern.
5. Runtime architecture
Experienced developers may ask, "Where is the DB connection?"
Use:
text
ADT
↓
ABAP backend
↓
ABAP SQL / database interface
↓
standard database connection
↓
system databaseSafe wording:
For ordinary ABAP SQL against system-managed data, the application developer does not create a JDBC/ODBC-style connection in the class.
Do not say "ABAP has no database connections." Advanced connection/external-data scenarios exist.
6. SQL Console in the lesson
Place the ADT SQL Console between Data Preview and the Reader class.
Recommended flow:
text
Data Preview
↓
inspect records
SQL Console
↓
run the ABAP SQL query interactively
Reader class
↓
use the query in organized application codeCurrent ADT documentation states that SQL Console can be opened from an ABAP project, Data Preview, a table editor, or a CDS editor. It displays the executed query, result, and query statistics. When opened from Data Preview, it can use the most recent Data Preview query. SQL Console supports the newer ABAP SQL syntax.
Use this query in the demonstration:
abap
SELECT FROM /dmo/carrier
FIELDS carrier_id,
name,
currency_code
ORDER BY carrier_idThen compare it with the Reader implementation:
abap
SELECT FROM /dmo/carrier
FIELDS carrier_id,
name,
currency_code
ORDER BY carrier_id
INTO TABLE @carriers.The comparison makes INTO TABLE concrete: SQL Console owns and displays its query result, while application code needs an ABAP target for the result.
Keep the terminology precise: this is the ADT SQL Console executing ABAP SQL in the ABAP development environment. Do not describe it as a direct SAP HANA SQL client.
7. Suggested timing
| Section | Time |
|---|---|
| Persistent data / Data Preview | 15 min |
| SQL Console exploration | 10 min |
| Runtime DB architecture | 10 min |
| ABAP SQL overview | 10 min |
| Why Runner + Reader | 10 min |
Create Reader types + GET_ALL | 20 min |
| Create Runner + call Reader | 15 min |
GET_BY_CURRENCY | 15 min |
GET_BY_ID + found handling | 15 min |
| Architecture recap | 10 min |
8. Carrier Reader reference implementation
abap
CLASS zcl_carrier_reader_### DEFINITION
PUBLIC
FINAL
CREATE PUBLIC.
PUBLIC SECTION.
TYPES:
BEGIN OF ty_carrier,
carrier_id TYPE /dmo/carrier-carrier_id,
name TYPE /dmo/carrier-name,
currency_code TYPE /dmo/carrier-currency_code,
END OF ty_carrier,
tt_carriers TYPE STANDARD TABLE OF ty_carrier
WITH EMPTY KEY.
METHODS get_all
RETURNING VALUE(carriers) TYPE tt_carriers.
METHODS get_by_currency
IMPORTING
currency TYPE /dmo/carrier-currency_code
RETURNING
VALUE(carriers) TYPE tt_carriers.
METHODS get_by_id
IMPORTING
carrier_id TYPE /dmo/carrier-carrier_id
EXPORTING
carrier TYPE ty_carrier
found TYPE abap_bool.
ENDCLASS.
CLASS zcl_carrier_reader_### IMPLEMENTATION.
METHOD get_all.
SELECT FROM /dmo/carrier
FIELDS carrier_id,
name,
currency_code
ORDER BY carrier_id
INTO TABLE @carriers.
ENDMETHOD.
METHOD get_by_currency.
SELECT FROM /dmo/carrier
FIELDS carrier_id,
name,
currency_code
WHERE currency_code = @currency
ORDER BY carrier_id
INTO TABLE @carriers.
ENDMETHOD.
METHOD get_by_id.
CLEAR:
carrier,
found.
SELECT SINGLE FROM /dmo/carrier
FIELDS carrier_id,
name,
currency_code
WHERE carrier_id = @carrier_id
INTO @carrier.
IF sy-subrc = 0.
found = abap_true.
ENDIF.
ENDMETHOD.
ENDCLASS.9. Carrier Runner reference implementation
abap
CLASS zcl_sql_l01_### DEFINITION
PUBLIC
FINAL
CREATE PUBLIC.
PUBLIC SECTION.
INTERFACES if_oo_adt_classrun.
ENDCLASS.
CLASS zcl_sql_l01_### IMPLEMENTATION.
METHOD if_oo_adt_classrun~main.
DATA(reader) = NEW zcl_carrier_reader_###( ).
DATA(all_carriers) = reader->get_all( ).
out->write( all_carriers ).
DATA currency TYPE /dmo/carrier-currency_code VALUE 'USD'.
DATA(filtered_carriers) =
reader->get_by_currency( currency = currency ).
out->write( filtered_carriers ).
DATA carrier_id TYPE /dmo/carrier-carrier_id VALUE 'LH'.
reader->get_by_id(
EXPORTING
carrier_id = carrier_id
IMPORTING
carrier = DATA(carrier)
found = DATA(found)
).
IF found = abap_true.
out->write( carrier ).
ELSE.
out->write( 'Carrier not found' ).
ENDIF.
ENDMETHOD.
ENDCLASS.Use demo values that actually exist in the tenant.
10. Why public result types?
The Runner needs a clear ABAP contract for what the Reader returns.
ty_carrier and tt_carriers provide that contract without exposing every persistence column.
Do not go deeply into DTO/domain-model terminology yet.
11. Why WITH EMPTY KEY?
For these simple read results, no ABAP internal-table lookup key is needed.
Do not confuse:
text
database table keywith:
text
ABAP internal table keyThey are different concepts.
12. Why does GET_BY_ID return found?
Returning only a structure would make "not found" indistinguishable from an initial structure.
A simple abap_bool keeps the first lesson explicit without introducing custom exceptions or references.
13. Where should sy-subrc be checked?
Keep sy-subrc inside the Reader immediately after the SELECT SINGLE that sets it.
Do not make the Runner inspect sy-subrc from a query that happened inside another method.
14. Assignment expected architecture
Students should first run the basic airport query in ADT SQL Console, then create:
text
ZCL_SQL_L01_PRACTICE_###
↓
ZCL_AIRPORT_READER_###
↓
/DMO/AIRPORTThe SQL Console step confirms the query independently of the class structure. The Reader then owns the corresponding SQL in application code.
The Runner should contain no direct airport SELECT.
15. Airport Reader reference solution
abap
CLASS zcl_airport_reader_### DEFINITION
PUBLIC
FINAL
CREATE PUBLIC.
PUBLIC SECTION.
TYPES:
BEGIN OF ty_airport,
airport_id TYPE /dmo/airport-airport_id,
name TYPE /dmo/airport-name,
city TYPE /dmo/airport-city,
country TYPE /dmo/airport-country,
END OF ty_airport,
tt_airports TYPE STANDARD TABLE OF ty_airport
WITH EMPTY KEY.
METHODS get_all
RETURNING VALUE(airports) TYPE tt_airports.
METHODS get_by_country
IMPORTING
country TYPE /dmo/airport-country
RETURNING
VALUE(airports) TYPE tt_airports.
METHODS get_by_id
IMPORTING
airport_id TYPE /dmo/airport-airport_id
EXPORTING
airport TYPE ty_airport
found TYPE abap_bool.
ENDCLASS.
CLASS zcl_airport_reader_### IMPLEMENTATION.
METHOD get_all.
SELECT FROM /dmo/airport
FIELDS airport_id,
name,
city,
country
ORDER BY airport_id
INTO TABLE @airports.
ENDMETHOD.
METHOD get_by_country.
SELECT FROM /dmo/airport
FIELDS airport_id,
name,
city,
country
WHERE country = @country
ORDER BY airport_id
INTO TABLE @airports.
ENDMETHOD.
METHOD get_by_id.
CLEAR:
airport,
found.
SELECT SINGLE FROM /dmo/airport
FIELDS airport_id,
name,
city,
country
WHERE airport_id = @airport_id
INTO @airport.
IF sy-subrc = 0.
found = abap_true.
ENDIF.
ENDMETHOD.
ENDCLASS.16. Airport Runner reference solution
abap
CLASS zcl_sql_l01_practice_### DEFINITION
PUBLIC
FINAL
CREATE PUBLIC.
PUBLIC SECTION.
INTERFACES if_oo_adt_classrun.
ENDCLASS.
CLASS zcl_sql_l01_practice_### IMPLEMENTATION.
METHOD if_oo_adt_classrun~main.
DATA(reader) = NEW zcl_airport_reader_###( ).
DATA(airports) = reader->get_all( ).
out->write( airports ).
DATA country TYPE /dmo/airport-country VALUE 'US'.
DATA(country_airports) =
reader->get_by_country( country = country ).
LOOP AT country_airports INTO DATA(airport).
out->write(
|{ airport-airport_id } - { airport-city } - { airport-name }|
).
ENDLOOP.
DATA airport_id TYPE /dmo/airport-airport_id VALUE '...'.
reader->get_by_id(
EXPORTING
airport_id = airport_id
IMPORTING
airport = DATA(one_airport)
found = DATA(found)
).
IF found = abap_true.
out->write( one_airport ).
ELSE.
out->write( 'Airport not found' ).
ENDIF.
ENDMETHOD.
ENDCLASS.17. Architecture questions — expected answers
What is the difference between SQL Console and the Reader?
SQL Console is an ADT development tool for executing and analyzing a query interactively. The Reader is application code that owns data-access logic and returns typed ABAP data to its caller.
Why does the Runner not contain SELECT?
Because the lesson separates execution/presentation from data access.
What is the Reader responsible for?
Reading data and encapsulating the ABAP SQL needed for that read.
What is the Runner responsible for?
Creating objects, calling Reader methods, and displaying/processing returned data.
Is the Reader a RAP business service?
No. It is a small OOP data-access class for this lesson.
Why no connection string?
Because the class executes in the ABAP backend, which provides the standard database access infrastructure for ordinary ABAP SQL.
Why return typed ABAP data?
To provide a clear method contract and keep presentation outside the Reader.
Why put WHERE in the Reader?
The Reader owns the query and should ask the database for only relevant rows.
Why SELECT SINGLE in GET_BY_ID?
Because the complete key identifies at most one row.
Why check sy-subrc in the Reader?
Because it immediately follows and interprets the SQL statement executed there.
18. Transition to Lesson 2
Lesson 1:
text
Runner
↓
Reader
↓
/DMO/CARRIERLesson 2 can become:
text
Runner
↓
Reader
↓
ZI_CARRIER_###
↓
/DMO/CARRIERThe Runner does not need to care whether the Reader reads a table or a CDS entity.
That provides a concrete reason for the data-access boundary.
19. Do not teach yet
Avoid:
- service classes;
- RAP service definitions;
- RAP service bindings;
- OData;
- table creation;
INSERT;UPDATE;DELETE;- repository-pattern theory;
- dependency-injection frameworks;
- joins and advanced SQL.