Appearance
Lesson 1 Companion
Use this companion when you want to go beyond the main lesson, review the architecture, compare your implementation with a reference solution, or understand why a particular design was chosen.
How to use this companion
Try the lesson and assignment first. Then use this file to check your reasoning, study a complete implementation, and review the architectural ideas in more depth.
Related Lesson 1 files:
- Lesson 1 — Database Basics, ABAP SQL, and Code Organization
- Lesson 1 Practice — Airport Reader and Runner
This companion reinforces the OOP knowledge used in the lesson.
1. Architectural goal
The architecture to avoid is:
text
Executable Class
↓
SELECT everywhere
↓
DatabaseThe lesson uses:
text
Runner
↓
Reader
↓
ABAP SQL
↓
PersistenceThis is a learning architecture, not the final RAP application architecture.
2. Why "Reader" and not "Service"
Reader is a more precise name here than 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?
Because the course has already introduced OOP, putting all SQL into IF_OO_ADT_CLASSRUN~main would discard the separation of responsibilities established earlier.
The Runner/Reader split reinforces:
- single responsibility;
- collaboration between objects;
- method boundaries;
- typed contracts;
- keeping data-access details out of presentation/execution code.
Treat this as a simple separation of responsibilities, not as a formal architectural pattern.
5. Runtime architecture
If you have worked with other application stacks, you may ask, "Where is the DB connection?"
The runtime path is:
text
ADT
↓
ABAP backend
↓
ABAP SQL / database interface
↓
standard database connection
↓
system databaseA precise description is:
For ordinary ABAP SQL against system-managed data, the application developer does not create a JDBC/ODBC-style connection in the class.
This does not mean that ABAP has no database connections; advanced connection and external-data scenarios also exist.
6. SQL Console in the lesson
The lesson places the ADT SQL Console between Data Preview and the Reader class.
The flow is:
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.
Start with this query in SQL Console:
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 study time
| 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.
DTO and domain-model terminology is not needed at this stage.
11. Why WITH EMPTY KEY?
For these simple read results, no ABAP internal-table lookup key is needed.
Keep the following concepts separate:
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?
Check sy-subrc inside the Reader immediately after the SELECT SINGLE that sets it.
The Runner should not inspect sy-subrc from a query that happened inside another method.
14. Assignment architecture
You 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 — Introduction to ABAP CDS evolves this into:
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. Topics that come later
These topics are deliberately deferred:
- service classes;
- RAP service definitions;
- RAP service bindings;
- OData;
- table creation;
INSERT;UPDATE;DELETE;- repository-pattern theory;
- dependency-injection frameworks;
- joins and advanced SQL.