Appearance
Lesson 5 — CDS Associations & Path Expressions
In Lesson 4, you combined related data with explicit joins:
text
/DMO/CONNECTION
├─ INNER JOIN Carrier
├─ INNER JOIN Origin Airport
└─ INNER JOIN Destination Airport
↓
Route modelThat solved an immediate problem: combine several sources into one result.
The relationships themselves are useful beyond one fixed result:
text
Connection → Carrier
Connection → Origin Airport
Connection → Destination AirportABAP CDS provides associations to model those relationships explicitly and make them reusable.
In this lesson, you will build:
text
/DMO/CONNECTION
↓
ZI_ROUTE_ASSOC_###
├── _Carrier ─────────────► /DMO/I_CARRIER
├── _OriginAirport ───────► /DMO/I_AIRPORT
└── _DestinationAirport ──► /DMO/I_AIRPORTYou will use path expressions to retrieve target fields, expose the associations themselves, and follow those associations directly in ADT Data Preview.
Lesson information
| Duration | About 120–150 minutes |
| Environment | SAP BTP ABAP Trial |
| Development tool | Eclipse with ABAP Development Tools (ADT) |
| Prerequisites | Lesson 1, Lesson 2, Lesson 3, and Lesson 4 |
| Primary source | /DMO/CONNECTION |
| Association targets | /DMO/I_CARRIER, /DMO/I_AIRPORT |
| CDS entity | ZI_ROUTE_ASSOC_### |
| Reader | ZCL_ROUTE_ASSOC_READER_### |
| Runner | ZCL_CDS_L05_### |
Learning objectives
After completing this lesson, you should be able to:
- explain what a CDS association represents;
- distinguish an explicit join from an association;
- explain that associations are directional;
- define a CDS association;
- identify its source and target;
- choose a meaningful association alias;
- explain
[0..1],[1..1],[0..*], and[1..*]; - choose cardinality based on the real relationship;
- write an association
ONcondition; - use
$projection; - explain why a to-one association should normally match the complete target key;
- use CDS path expressions;
- explain what happens when a path target field is used;
- expose an association in the element list;
- follow an exposed association in ADT Data Preview;
- distinguish a scalar path field from an exposed relationship;
- query scalar association-derived fields in SQL Console;
- consume the CDS model through a Reader;
- explain how associations prepare the model for RAP and service navigation.
Before you start
Replace ### with your student/group suffix.
Example:
text
ZI_ROUTE_ASSOC_### → ZI_ROUTE_ASSOC_017
ZCL_ROUTE_ASSOC_READER_### → ZCL_ROUTE_ASSOC_READER_017
ZCL_CDS_L05_### → ZCL_CDS_L05_017ABAP Cloud and /DMO/*
The /DMO/* artifacts used here are SAP Flight Reference Scenario demo/tutorial content.
For productive ABAP Cloud development, consume SAP functionality through APIs released for the applicable cloud-development contract.
1. Revisit the Lesson 4 model
Lesson 4 used explicit joins:
abap
inner join /dmo/carrier as Carrier
on Connection.carrier_id = Carrier.carrier_id
inner join /dmo/airport as OriginAirport
on Connection.airport_from_id = OriginAirport.airport_id
inner join /dmo/airport as DestinationAirport
on Connection.airport_to_id = DestinationAirport.airport_idThis says:
For this CDS result, combine these sources now.
Associations let us say something more reusable:
text
A Connection has a Carrier relationship.
A Connection has an Origin Airport relationship.
A Connection has a Destination Airport relationship.2. What is a CDS association?
A CDS association is a named relationship from an association source to an association target.
Conceptually:
text
Connection ── _Carrier ──► CarrierAn association has:
text
target
name/alias
cardinality
ON conditionThe ON condition describes how source and target records correspond.
TIP
Think of an association as a named navigation path between data entities.
3. Associations are directional
If you define:
text
Connection → Carrierthat does not automatically define:
text
Carrier → ConnectionThe association belongs to its source entity.
If reverse navigation is needed, it needs its own model relationship.
This becomes important later when RAP models parent/child relationships explicitly.
4. Why this lesson uses CDS targets
This lesson targets SAP's demo CDS entities:
text
/DMO/I_CARRIER
/DMO/I_AIRPORTrather than directly targeting the database tables.
The Flight Reference Scenario exposes:
text
/DMO/I_CARRIER
AirlineID
Name
CurrencyCodeand:
text
/DMO/I_AIRPORT
AirportID
Name
City
CountryCodeThis gives a useful architecture:
text
Your CDS entity
↓ association
Reusable target CDS entity
↓
Persistence5. Inspect /DMO/I_CARRIER
Press:
text
Ctrl + Shift + ASearch for:
text
/DMO/I_CARRIEROpen its Data Definition.
Identify:
text
AirlineID
Name
CurrencyCodeThe target key is:
text
AirlineIDUse Data Preview if useful.
6. Inspect /DMO/I_AIRPORT
Open:
text
/DMO/I_AIRPORTIdentify:
text
AirportID
Name
City
CountryCodeThe target key is:
text
AirportIDBefore writing any association, you should understand the key of its target.
7. Association syntax
A basic association looks like:
abap
association [1..1] to /DMO/I_Carrier as _Carrier
on <condition>Break it down:
| Part | Meaning |
|---|---|
association | define a relationship |
[1..1] | target cardinality |
to | target follows |
/DMO/I_Carrier | target CDS entity |
_Carrier | association name |
on | relationship condition |
8. Association naming
This course uses names such as:
text
_Carrier
_OriginAirport
_DestinationAirportThe leading underscore is a common SAP CDS naming style for associations.
The underscore is not a CDS keyword and is not what makes something an association.
Use names that describe the target's role.
9. Cardinality
Cardinality describes how many target records are expected for one source record.
Common forms:
| Cardinality | Meaning |
|---|---|
[0..1] | zero or one |
[1..1] | exactly one |
[0..*] | zero, one, or many |
[1..*] | one or many |
For the demo connection model:
text
one Connection → one Carrier
one Connection → one Origin Airport
one Connection → one Destination AirportUse:
text
[1..1]for these relationships.
Cardinality is part of the model
Do not copy [1..1] mechanically.
Cardinality must agree with the real relationship and the ON condition.
10. Create ZI_ROUTE_ASSOC_###
Right-click your package:
text
New
→ Other ABAP Repository Object
→ Core Data Services
→ Data DefinitionEnter:
text
Name: ZI_ROUTE_ASSOC_###
Description: Lesson 5 - Route AssociationsChoose the course transport if required.
Select a CDS View Entity template.
Confirm:
abap
define view entityand finish.
11. Start with the Connection fields
Enter:
abap
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Route with Associations'
define view entity ZI_Route_Assoc_###
as select from /dmo/connection as Connection
{
key Connection.carrier_id as CarrierId,
key Connection.connection_id as ConnectionId,
Connection.airport_from_id as OriginAirportId,
Connection.airport_to_id as DestinationAirportId,
Connection.departure_time as DepartureTime,
Connection.arrival_time as ArrivalTime,
Connection.distance as Distance,
Connection.distance_unit as DistanceUnit
}Replace ###.
Check:
text
Ctrl + F2Activate:
text
Ctrl + F3Preview:
text
F812. Add _Carrier
Place the association between the data source and the element list:
abap
association [1..1] to /DMO/I_Carrier as _Carrier
on $projection.CarrierId = _Carrier.AirlineIDYour source section becomes:
abap
define view entity ZI_Route_Assoc_###
as select from /dmo/connection as Connection
association [1..1] to /DMO/I_Carrier as _Carrier
on $projection.CarrierId = _Carrier.AirlineID
{
...
}13. What is $projection?
The persistence source provides:
text
Connection.carrier_idYour CDS entity exposes it as:
text
CarrierId$projection refers to an element of the current CDS projection:
abap
$projection.CarrierIdSo:
abap
on $projection.CarrierId = _Carrier.AirlineIDmeans:
text
current entity CarrierId
=
associated carrier AirlineIDThis keeps the relationship aligned with the current CDS model's element names.
14. Complete target-key reasoning
/DMO/I_CARRIER is identified by:
text
AirlineIDThe association matches:
text
CarrierId → AirlineIDThat is a complete target-key relationship.
For a to-one association, checking the target key is essential.
You will see a composite target key in the practice assignment.
15. Use the association with a path expression
Add:
abap
_Carrier.Name as CarrierName,This path expression contains:
text
_Carrier
↓
NameRead it as:
Navigate through
_Carrierand use the target'sNameelement.
The resulting scalar element in your entity is:
text
CarrierName16. Activate and preview
Run:
text
Ctrl + F2
Ctrl + F3
F8You should now see:
text
CarrierId
CarrierNameCarrierName is retrieved through the association path.
17. What happens when a path target field is used?
The association itself defines the relationship.
When you request a target field:
abap
_Carrier.Namethe relationship must be instantiated to retrieve the target data.
Current CDS behavior uses a left outer join by default for a path target field in a CDS element list.
Conceptually:
text
association declaration
=
relationship model
path target field
=
use relationship to retrieve target value18. Join vs association
Lesson 4:
text
Connection
INNER JOIN CarrierThe join is fixed into the source expression.
Lesson 5:
text
Connection
association → CarrierThe relationship can then be:
text
used through a path
exposed for navigation
reused by consumersAssociations are still translated into database operations when used. They are a higher-level relationship model, not a replacement for relational execution.
19. Add _OriginAirport
Add:
abap
association [1..1] to /DMO/I_Airport as _OriginAirport
on $projection.OriginAirportId = _OriginAirport.AirportIDThe relationship is:
text
OriginAirportId → AirportID20. Add Origin path fields
Add:
abap
_OriginAirport.Name as OriginAirportName,
_OriginAirport.City as OriginCity,
_OriginAirport.CountryCode as OriginCountryCode,Activate and preview.
Verify that the airport ID and descriptive values match.
21. Add _DestinationAirport
Add:
abap
association [1..1] to /DMO/I_Airport as _DestinationAirport
on $projection.DestinationAirportId = _DestinationAirport.AirportIDThe same target CDS entity is now used in another role.
Role-based names make the distinction clear:
text
_OriginAirport
_DestinationAirport22. Add Destination path fields
Add:
abap
_DestinationAirport.Name as DestinationAirportName,
_DestinationAirport.City as DestinationCity,
_DestinationAirport.CountryCode as DestinationCountryCode,Activate and preview.
23. Model before association exposure
Your entity can now look like:
abap
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Route with Associations'
define view entity ZI_Route_Assoc_###
as select from /dmo/connection as Connection
association [1..1] to /DMO/I_Carrier as _Carrier
on $projection.CarrierId = _Carrier.AirlineID
association [1..1] to /DMO/I_Airport as _OriginAirport
on $projection.OriginAirportId = _OriginAirport.AirportID
association [1..1] to /DMO/I_Airport as _DestinationAirport
on $projection.DestinationAirportId = _DestinationAirport.AirportID
{
key Connection.carrier_id as CarrierId,
key Connection.connection_id as ConnectionId,
_Carrier.Name as CarrierName,
Connection.airport_from_id as OriginAirportId,
_OriginAirport.Name as OriginAirportName,
_OriginAirport.City as OriginCity,
_OriginAirport.CountryCode as OriginCountryCode,
Connection.airport_to_id as DestinationAirportId,
_DestinationAirport.Name as DestinationAirportName,
_DestinationAirport.City as DestinationCity,
_DestinationAirport.CountryCode as DestinationCountryCode,
Connection.departure_time as DepartureTime,
Connection.arrival_time as ArrivalTime,
Connection.distance as Distance,
Connection.distance_unit as DistanceUnit
}This uses associations internally, but the relationships themselves are not yet exposed.
24. Expose an association
Add the association name to the element list:
abap
_CarrierDo the same for:
abap
_OriginAirport
_DestinationAirportNear the end of the element list:
abap
_Carrier,
_OriginAirport,
_DestinationAirportNow the relationships themselves are part of the public CDS model.
25. Final ZI_ROUTE_ASSOC_###
abap
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Route with Associations'
define view entity ZI_Route_Assoc_###
as select from /dmo/connection as Connection
association [1..1] to /DMO/I_Carrier as _Carrier
on $projection.CarrierId = _Carrier.AirlineID
association [1..1] to /DMO/I_Airport as _OriginAirport
on $projection.OriginAirportId = _OriginAirport.AirportID
association [1..1] to /DMO/I_Airport as _DestinationAirport
on $projection.DestinationAirportId = _DestinationAirport.AirportID
{
key Connection.carrier_id as CarrierId,
key Connection.connection_id as ConnectionId,
_Carrier.Name as CarrierName,
Connection.airport_from_id as OriginAirportId,
_OriginAirport.Name as OriginAirportName,
_OriginAirport.City as OriginCity,
_OriginAirport.CountryCode as OriginCountryCode,
Connection.airport_to_id as DestinationAirportId,
_DestinationAirport.Name as DestinationAirportName,
_DestinationAirport.City as DestinationCity,
_DestinationAirport.CountryCode as DestinationCountryCode,
Connection.departure_time as DepartureTime,
Connection.arrival_time as ArrivalTime,
Connection.distance as Distance,
Connection.distance_unit as DistanceUnit,
_Carrier,
_OriginAirport,
_DestinationAirport
}Check and activate:
text
Ctrl + F2
Ctrl + F326. Scalar path field vs exposed association
Compare:
abap
_Carrier.Name as CarrierNamewith:
abap
_CarrierThe first creates a scalar result element:
text
CarrierNameThe second exposes the relationship itself.
Think:
text
_Carrier.Name
=
give this entity a CarrierName field
_Carrier
=
make the Carrier relationship navigable27. Follow an association in Data Preview
Open Data Preview:
text
F8Now use the ADT association-navigation feature.
Step 27.1
Open:
text
Follow Associationfrom Data Preview.
Depending on ADT layout/version, this can be available from the table context menu or navigation/breadcrumb controls.
Step 27.2
Choose:
text
_CarrierADT navigates to the associated target data.
You should see target fields such as:
text
AirlineID
Name
CurrencyCodeStep 27.3
Use the breadcrumb/navigation control to return to:
text
ZI_ROUTE_ASSOC_###Step 27.4
Follow:
text
_OriginAirportInspect:
text
AirportID
Name
City
CountryCodeStep 27.5
Repeat for:
text
_DestinationAirportTIP
An exposed association is now visible as an actual navigation relationship in the development tooling.
28. Why exposed associations matter
Without exposing _Carrier, your entity can still use:
abap
_Carrier.Nameinternally.
Exposing:
abap
_Carrieradds the relationship itself to the entity's public model.
That matters later for:
text
other CDS models
projection layers
service navigation
RAP models29. Path expressions can be longer
Conceptually, path expressions can navigate through more than one association:
text
_Association1._Association2.FieldFor example:
text
Booking
↓ _Customer
Customer
↓ _Country
CountryThe exact allowed use depends on cardinality, context, filters, and path-expression rules.
This lesson deliberately uses one association step at a time.
30. To-one vs to-many
Our guided associations are to-one:
text
Connection → Carrier
Connection → Origin Airport
Connection → Destination AirportA different direction can be to-many:
text
Carrier → ConnectionsOne carrier can have several connections.
That could be modeled as:
text
[0..*]The practice includes this as an optional challenge.
31. Why cardinality matters
Do not treat cardinality as a comment.
It influences how CDS understands the relationship and can influence generated SQL/optimization behavior.
Before declaring to-one, ask:
text
Can zero targets exist?
Can multiple targets exist?
Does the ON condition match the target key?32. Path target join attributes
The default CDS path target field uses left outer join semantics.
Current CDS also supports a path attribute such as:
abap
_Carrier[inner].Nameto request inner-join behavior for that path.
Do not change the lesson model to this form.
The important point is to understand that:
text
association cardinalityand:
text
join behavior when a path is usedare related but different concepts.
33. SQL Console
Open SQL Console and query:
abap
SELECT FROM zi_route_assoc_###
FIELDS CarrierId,
ConnectionId,
CarrierName,
OriginAirportId,
OriginCity,
DestinationAirportId,
DestinationCity
ORDER BY CarrierId,
ConnectionIdThe consumer sees ordinary scalar elements.
It does not repeat the associations or path expressions.
34. Filter a path-derived element
Use a country code that exists in your tenant.
Example:
abap
SELECT FROM zi_route_assoc_###
FIELDS CarrierId,
ConnectionId,
CarrierName,
OriginCity,
OriginCountryCode,
DestinationCity,
DestinationCountryCode
WHERE OriginCountryCode = 'DE'
ORDER BY CarrierId,
ConnectionIdOriginCountryCode was derived through:
text
_OriginAirport.CountryCodebut the SQL consumer uses the exposed scalar name.
35. Create ZCL_ROUTE_ASSOC_READER_###
Create a normal ABAP class.
Do not implement:
text
IF_OO_ADT_CLASSRUNDefine:
abap
CLASS zcl_route_assoc_reader_### DEFINITION
PUBLIC
FINAL
CREATE PUBLIC.
PUBLIC SECTION.
TYPES tt_routes
TYPE STANDARD TABLE OF zi_route_assoc_###
WITH EMPTY KEY.
ENDCLASS.36. Add Reader methods
Add:
abap
METHODS get_all
RETURNING VALUE(routes) TYPE tt_routes.
METHODS get_from_country
IMPORTING
country_code TYPE /dmo/airport-country
RETURNING
VALUE(routes) TYPE tt_routes.37. Implement GET_ALL
abap
METHOD get_all.
SELECT FROM zi_route_assoc_###
FIELDS *
ORDER BY CarrierId,
ConnectionId
INTO TABLE @routes.
ENDMETHOD.The Reader contains no relationship definition.
38. Implement GET_FROM_COUNTRY
abap
METHOD get_from_country.
SELECT FROM zi_route_assoc_###
FIELDS *
WHERE OriginCountryCode = @country_code
ORDER BY CarrierId,
ConnectionId
INTO TABLE @routes.
ENDMETHOD.A scalar path-derived CDS field can be filtered like any other exposed element.
39. Complete Reader
abap
CLASS zcl_route_assoc_reader_### DEFINITION
PUBLIC
FINAL
CREATE PUBLIC.
PUBLIC SECTION.
TYPES tt_routes
TYPE STANDARD TABLE OF zi_route_assoc_###
WITH EMPTY KEY.
METHODS get_all
RETURNING VALUE(routes) TYPE tt_routes.
METHODS get_from_country
IMPORTING
country_code TYPE /dmo/airport-country
RETURNING
VALUE(routes) TYPE tt_routes.
ENDCLASS.
CLASS zcl_route_assoc_reader_### IMPLEMENTATION.
METHOD get_all.
SELECT FROM zi_route_assoc_###
FIELDS *
ORDER BY CarrierId,
ConnectionId
INTO TABLE @routes.
ENDMETHOD.
METHOD get_from_country.
SELECT FROM zi_route_assoc_###
FIELDS *
WHERE OriginCountryCode = @country_code
ORDER BY CarrierId,
ConnectionId
INTO TABLE @routes.
ENDMETHOD.
ENDCLASS.Activate it.
40. Create the Runner
Create:
text
ZCL_CDS_L05_###Definition:
abap
CLASS zcl_cds_l05_### DEFINITION
PUBLIC
FINAL
CREATE PUBLIC.
PUBLIC SECTION.
INTERFACES if_oo_adt_classrun.
ENDCLASS.41. Consume the Reader
abap
CLASS zcl_cds_l05_### IMPLEMENTATION.
METHOD if_oo_adt_classrun~main.
DATA(reader) = NEW zcl_route_assoc_reader_###( ).
DATA(routes) = reader->get_all( ).
LOOP AT routes INTO DATA(route).
out->write(
|{ route-CarrierName }: | &&
|{ route-OriginCity } -> { route-DestinationCity }|
).
ENDLOOP.
ENDMETHOD.
ENDCLASS.Activate and run:
text
F942. Test the Reader filter
Choose a country code present in Data Preview.
Example:
abap
DATA country_code TYPE /dmo/airport-country VALUE 'DE'.
DATA(routes_from_country) =
reader->get_from_country(
country_code = country_code
).
out->write( routes_from_country ).Use a value that exists in your own tenant.
43. Associations are part of CDS, not RAP behavior
A normal CDS association models a data relationship.
It is not:
text
a RAP action
a validation
a determination
a service bindingRAP later uses CDS relationships heavily, but the association itself is a CDS modeling concept.
44. Associations vs compositions
Later RAP lessons introduce:
text
composition
association to parentThose are specialized relationship concepts for business-object hierarchies.
This lesson focuses on normal associations:
abap
association ... to ...45. Common mistakes
Using target persistence names
Target:
text
/DMO/I_CARRIERexposes:
text
AirlineID
Name
CurrencyCodeDo not write:
text
carrier_id
currency_codeagainst the CDS target.
Forgetting $projection
The current model alias:
text
CarrierIdis referenced as:
abap
$projection.CarrierIdinside the association condition.
Using the wrong airport relationship
Wrong:
abap
_OriginAirport.Name as DestinationAirportNameCorrect:
abap
_DestinationAirport.Name as DestinationAirportNameDeclaring every relationship [1..1]
Choose cardinality based on the real relationship.
Declaring to-one without matching the target key
If several target rows can match, the relationship is not really to-one.
Forgetting that path target fields perform data access
This:
abap
_Carrier.Namecauses target data to be retrieved.
The association is not merely documentation.
Forgetting to expose the association
This:
abap
_Carrier.Name as CarrierNamedoes not automatically expose the _Carrier relationship.
Add:
abap
_Carrierif navigation should be public.
Assuming associations are automatically bidirectional
They are directional.
46. Knowledge check
1. What does an association define?
Show answer
A named directional relationship from a CDS source to a target, together with cardinality and an ON condition.
2. What does [1..1] mean?
Show answer
Exactly one target record is expected for one source record.
3. What does [0..*] mean?
Show answer
Zero, one, or many target records may exist for one source record.
4. What is $projection.CarrierId?
Show answer
A reference to the current CDS entity's projected element CarrierId.
5. What does _Carrier.Name mean?
Show answer
Navigate the _Carrier association and use the target's Name element.
6. What is the default join behavior of a CDS path target field in the element list?
Show answer
Left outer join semantics.
7. What does _Carrier by itself in the element list do?
Show answer
It exposes the association so supported consumers can navigate the relationship.
8. Are associations automatically bidirectional?
Show answer
No.
9. Why should a to-one association match the complete target key?
Show answer
Otherwise several target records may match one source record, contradicting the declared relationship.
10. Why did joins come before associations in this course?
Show answer
Joins make row matching, ON conditions, join types, and row multiplication explicit. Associations then add a reusable relationship abstraction over those concepts.
47. Lesson summary
You moved from:
text
fixed explicit joinsto:
text
named reusable relationshipsYou learned:
text
association
source and target
association aliases
cardinality
ON conditions
$projection
path expressions
path target fields
association exposure
Follow Association in ADT
default left outer path behavior
Reader consumptionThe final model is:
text
/DMO/CONNECTION
↓
ZI_ROUTE_ASSOC_###
├── _Carrier [1..1] ───────────► /DMO/I_CARRIER
├── _OriginAirport [1..1] ─────► /DMO/I_AIRPORT
└── _DestinationAirport [1..1] ► /DMO/I_AIRPORTThe central idea is:
A CDS association gives a relationship a name, cardinality, condition, and navigation path so it can be reused instead of existing only as a fixed join.
48. What comes next?
Before moving on, complete the Lesson 5 practice assignment to apply associations to booking, customer, carrier, and connection data. Use the Lesson 5 companion for deeper explanations, troubleshooting, and reference implementations.
The model now contains:
text
data
calculations
filters
joins
associations
navigationLesson 6 — CDS Semantics & Annotations adds semantic metadata:
text
amount + currency
quantity + unit
text elements
text associations
annotation propagation
business meaningThat prepares the CDS layer for later service consumption.