Appearance
Lesson 1 — Database Basics, ABAP SQL, and Code Organization
Until now, you have been working with ABAP syntax and object-oriented programming.
In this lesson, you will connect that knowledge to persistent data.
You will learn this small architecture:
text
Database Table
↓
ABAP SQL
↓
Reader Class
↓
Runner Class
↓
ConsoleThe goal is not only to make a SELECT work. The goal is also to give classes clear responsibilities.
Scope
This lesson covers read access with SELECT, together with code organization for database access.
Table creation, INSERT, UPDATE, DELETE, and RAP business services are outside the scope of this lesson.
Lesson information
| Duration | About 90–120 minutes |
| Environment | SAP BTP ABAP Trial |
| Tooling | Eclipse + ADT |
| Prerequisites | ABAP syntax and OOP |
| Demo table | /DMO/CARRIER |
| Output | ABAP Console |
Learning objectives
After this lesson, you should be able to:
- explain persistent database data;
- distinguish a database table from an ABAP internal table;
- inspect a database table in ADT and use Data Preview;
- open and use the ADT SQL Console to explore an ABAP SQL query;
- explain where ABAP code executes and why ordinary ABAP SQL needs no JDBC/ODBC-style connection string;
- use
SELECT,FIELDS,WHERE,ORDER BY, host variables, andSELECT SINGLE; - define a small result structure and internal-table type;
- create a Reader class responsible for data access;
- create a Runner class responsible for execution and console output;
- call Reader methods from the Runner;
- explain why this Reader is not a RAP business service.
Replace ###
Use your own student/group suffix:
text
ZCL_CARRIER_READER_### → ZCL_CARRIER_READER_017
ZCL_SQL_L01_### → ZCL_SQL_L01_017ABAP Cloud mindset
The /DMO/* objects belong to SAP's ABAP Flight Reference Scenario and are demo/tutorial content.
In productive ABAP Cloud development, do not assume that every SAP repository object can be consumed directly. Released APIs and cloud-development contracts matter.
Do not create your own objects in the /DMO/ namespace.
1. Persistent data
An ABAP variable exists while your code runs:
abap
DATA name TYPE string.
name = 'Lufthansa'.Business applications also need data that remains available independently of one execution. That is persistent data.
A database table stores persistent rows and columns. One or more key fields identify records.
2. Explore /DMO/CARRIER
Open:
text
/DMO/CARRIERwith:
text
Ctrl + Shift + AFind these fields:
| Field | Meaning | Key? |
|---|---|---|
carrier_id | Carrier identifier | Yes |
name | Carrier name | No |
currency_code | Currency code | No |
Open Data Preview and write down one carrier ID and one currency code that actually exist in your system.
For supported table/view contexts, ADT also provides:
text
Alt + F83. Explore the query in SQL Console
Data Preview is useful when you want to inspect records. ADT also provides an SQL Console where you can write and execute ABAP SQL queries directly.
Open SQL Console in one of these ways:
- from the Data Preview editor, choose the SQL Console tab;
- right-click
/DMO/CARRIERand choose Open With → SQL Console; - right-click the ABAP project and choose SQL Console.
When SQL Console is opened from Data Preview, ADT can carry over the current query from Data Preview.
Enter this query:
abap
SELECT FROM /dmo/carrier
FIELDS carrier_id,
name,
currency_code
ORDER BY carrier_idRun the query.
SQL Console displays the query result and query statistics. It supports the modern ABAP SQL syntax used in this course.
Notice that the SQL Console query has no INTO TABLE.
The SQL Console itself displays the result. There is no ABAP program variable receiving it.
Later, the same query inside a Reader method will need an ABAP target:
abap
INTO TABLE @carriersThis gives us a useful distinction:
text
Data Preview
→ inspect existing data
SQL Console
→ write and execute a query interactively
Reader class
→ use the query as part of application codeTIP
Use SQL Console to inspect and experiment with ABAP SQL queries. Put application data-access logic in the classes and data-model objects that belong to the application.
4. Database table vs internal table
A database table is persistent:
text
/DMO/CARRIERAn ABAP internal table is an ABAP data object in program memory.
The flow is:
text
Database Table
│
│ SELECT
▼
ABAP Internal TableThey can both contain rows, but they live in different places and have different responsibilities.
5. Where is the database connection?
If you know Java, Python, C#, or Node.js, you may expect:
text
Application
↓
Driver
↓
Connection string / credentials
↓
DatabaseOrdinary ABAP SQL in this lesson works differently.
Your class runs in the ABAP backend system, not locally inside Eclipse.
text
Your computer
Eclipse + ADT
↓
ABAP Cloud Project
↓
ABAP backend
↓
Your ABAP code
↓
ABAP SQL / database interface
↓
standard database connection
↓
system databaseThat is why this contains no hostname, driver, username, password, or connection string:
abap
SELECT FROM /dmo/carrierTIP
ADT connects to the ABAP backend. Your ABAP code executes there, and the backend performs the database access.
WARNING
ABAP also supports advanced external-data and additional-connection scenarios. They are outside this lesson.
6. What is ABAP SQL?
ABAP provides ABAP SQL for reading SQL data sources.
Older material may call it Open SQL.
Example:
abap
SELECT FROM /dmo/carrier
FIELDS carrier_id,
name,
currency_code
ORDER BY carrier_id
INTO TABLE @DATA(carriers).For this course, however, we will not leave the database code in the executable class.
7. Why not put everything in one class?
This would work:
text
Console class
├─ SELECT
├─ filters
├─ loops
└─ outputBut it gives one class several responsibilities.
Instead:
text
ZCL_SQL_L01_###
Runner
↓ calls
ZCL_CARRIER_READER_###
Reader
↓ ABAP SQL
/DMO/CARRIERThe Runner starts the example, creates objects, calls methods, and displays results.
The Reader knows how carrier data is read and contains the ABAP SQL.
OOP principle
The Runner is responsible for running the example.
The Reader is responsible for reading carrier data.
8. Is the Reader a service?
No.
We deliberately call it a Reader, not a "service class" or "business service."
Later in RAP, business service has a specific meaning involving concepts such as:
text
CDS data model
RAP behavior
service definition
service bindingThe Reader is an OOP data-access abstraction. A RAP business service is a different architectural concept with its own repository objects and responsibilities.
9. Create the Reader class
Create:
text
ZCL_CARRIER_READER_###Description:
text
Lesson 1 - Carrier ReaderIt does not implement IF_OO_ADT_CLASSRUN.
10. Define public result types
Use:
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.
ENDCLASS.ty_carrier represents one result row.
tt_carriers represents many result rows.
11. Add GET_ALL
Add:
abap
METHODS get_all
RETURNING VALUE(carriers) TYPE tt_carriers.Complete definition:
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.
ENDCLASS.12. Implement GET_ALL
abap
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.
ENDCLASS.carriers already exists as the returning parameter, so the SQL result is written into it.
13. Understand the SELECT
abap
SELECT FROM /dmo/carrierselects the data source.
abap
FIELDS carrier_id,
name,
currency_coderequests only the fields we need.
abap
ORDER BY carrier_iddefines the row order. Without ORDER BY, result order is not guaranteed.
abap
INTO TABLE @carriersplaces the multi-row result into an ABAP internal table.
Activate the Reader with:
text
Ctrl + F314. Create the Runner
Create:
text
ZCL_SQL_L01_###This class does implement:
text
IF_OO_ADT_CLASSRUNStart with:
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.
out->write( 'Lesson 1' ).
ENDMETHOD.
ENDCLASS.Activate and run with:
text
F915. Create and use the Reader object
Replace main with:
abap
METHOD if_oo_adt_classrun~main.
DATA(reader) = NEW zcl_carrier_reader_###( ).
DATA(carriers) = reader->get_all( ).
out->write( carriers ).
ENDMETHOD.Now the flow is:
text
F9
↓
Runner
↓
Reader object
↓
GET_ALL
↓
ABAP SQL
↓
/DMO/CARRIER
↓
Reader returns internal table
↓
Runner writes outputThe Runner contains no direct carrier SELECT.
16. Add GET_BY_CURRENCY
In the Reader definition:
abap
METHODS get_by_currency
IMPORTING
currency TYPE /dmo/carrier-currency_code
RETURNING
VALUE(carriers) TYPE tt_carriers.Implementation:
abap
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.17. Host variables and @
In:
abap
WHERE currency_code = @currencycurrency_code is a SQL source field.
currency is an ABAP method parameter.
ABAP host variables inside ABAP SQL are marked with @.
18. Call GET_BY_CURRENCY
In the Runner:
abap
METHOD if_oo_adt_classrun~main.
DATA(reader) = NEW zcl_carrier_reader_###( ).
DATA currency TYPE /dmo/carrier-currency_code VALUE 'USD'.
DATA(carriers) =
reader->get_by_currency( currency = currency ).
out->write( carriers ).
ENDMETHOD.Use a currency that exists in your Data Preview.
The filtering stays in the Reader because the Reader owns the query.
19. Add GET_BY_ID
Add to the Reader:
abap
METHODS get_by_id
IMPORTING
carrier_id TYPE /dmo/carrier-carrier_id
EXPORTING
carrier TYPE ty_carrier
found TYPE abap_bool.Implementation:
abap
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.20. Why SELECT SINGLE?
carrier_id is the complete key of /DMO/CARRIER.
Therefore:
abap
WHERE carrier_id = @carrier_ididentifies at most one carrier record.
That is an appropriate use of SELECT SINGLE.
Do not think of it as "give me any one matching row."
21. Call GET_BY_ID
Use a carrier ID that exists in your system.
abap
METHOD if_oo_adt_classrun~main.
DATA(reader) = NEW zcl_carrier_reader_###( ).
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.Replace 'LH' if necessary.
Test both an existing and a non-existing ID.
22. Why is sy-subrc checked in the Reader?
The Reader performs the SELECT SINGLE, so it checks the immediate database result.
The Runner receives a simpler API:
text
carrier
foundThis keeps database-specific handling close to the database-access code.
23. Process returned data
Returned data is normal ABAP data:
abap
DATA(carriers) = reader->get_all( ).
LOOP AT carriers INTO DATA(carrier).
out->write(
|{ carrier-carrier_id } - { carrier-name }|
).
ENDLOOP.The Reader reads.
The Runner decides how to present the result.
24. Lesson 1 architecture
text
┌────────────────────────────┐
│ ZCL_SQL_L01_### │
│ Runner │
│ - creates Reader │
│ - calls Reader methods │
│ - writes console output │
└──────────────┬─────────────┘
│
▼
┌────────────────────────────┐
│ ZCL_CARRIER_READER_### │
│ Reader │
│ - GET_ALL │
│ - GET_BY_CURRENCY │
│ - GET_BY_ID │
│ - contains ABAP SQL │
└──────────────┬─────────────┘
│
▼
┌────────────────────────────┐
│ /DMO/CARRIER │
│ Persistent data │
└────────────────────────────┘25. Where is this going?
Today:
text
Runner
↓
Reader
↓
Database TableNext, CDS adds a reusable data-model layer:
text
Runner
↓
Reader
↓
CDS View Entity
↓
Database TableLater, RAP introduces a different and richer architecture:
text
Consumer
↓
Business Service
↓
RAP Behavior
↓
CDS Data Model
↓
PersistenceThe Reader is useful for learning OOP responsibility and data access. It is not a replacement for RAP architecture.
26. Common mistakes
Putting SQL back in the Runner
Keep carrier SQL in:
text
ZCL_CARRIER_READER_###Calling the Reader a business service
Do not name it ZCL_CARRIER_SERVICE_### in this lesson. RAP business service means something different.
Missing @
Wrong:
abap
WHERE currency_code = currencyCorrect:
abap
WHERE currency_code = @currencyAssuming database order
If order matters:
abap
ORDER BY carrier_idUsing SELECT * by default
Prefer explicit fields when only certain columns are required.
Treating SQL Console as application code
SQL Console is an ADT development tool for running and analyzing queries. The query you test there still needs to be placed in the appropriate application data-access code when the application needs it.
Looking for a connection string
The Runner and Reader execute in the ABAP backend. Ordinary ABAP SQL uses the platform's database access infrastructure.
27. Summary
You learned two architectures.
Runtime:
text
ADT
↓
ABAP backend
↓
ABAP SQL / database interface
↓
System databaseDevelopment workflow:
text
Data Preview → inspect data
SQL Console → explore a query
Reader → application data accessCode organization:
text
Runner
↓
Reader
↓
ABAP SQL
↓
Database TableThe main lesson is:
Learning database access does not mean putting database code everywhere.
For this lesson, the Reader owns data access and the Runner owns execution/output.
Next, CDS will give the Reader a reusable data model to read from.