RAP from scratch · Lesson 2 of 6
RAP data model: tables, CDS View Entities and associations
Create the tables for a library, load test data and define the Root View Entity with its associations: the foundation of any RAP business object.
Every RAP application is built in layers, bottom-up:
- Data: database tables and CDS View Entities (this lesson).
- Projection and service: what is exposed to the outside (lesson 3).
- Behavior: what can be done with the data, and under which rules (lesson 4).
We keep the library from the previous lesson: authors and books.
1. The tables
In ADT: New → Other ABAP Repository Object → Dictionary → Database Table.
Authors, a simple lookup table:
@EndUserText.label : 'Library: authors'
@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE
@AbapCatalog.tableCategory : #TRANSPARENT
@AbapCatalog.deliveryClass : #A
@AbapCatalog.dataMaintenance : #RESTRICTED
define table zlib_author {
key client : abap.clnt not null;
key author_id : abap.numc(6) not null;
name : abap.char(60);
country : abap.char(2);
}
Books, which will become the RAP business object:
@EndUserText.label : 'Library: books'
@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE
@AbapCatalog.tableCategory : #TRANSPARENT
@AbapCatalog.deliveryClass : #A
@AbapCatalog.dataMaintenance : #RESTRICTED
define table zlib_book {
key client : abap.clnt not null;
key book_uuid : sysuuid_x16 not null;
book_id : abap.numc(8);
title : abap.char(80);
author_id : abap.numc(6);
pages : abap.int4;
isbn : abap.char(17);
rating : abap.int1;
@Semantics.amount.currencyCode : 'zlib_book.currency_code'
price : abap.curr(11,2);
currency_code : abap.cuky;
local_created_by : abp_creation_user;
local_created_at : abp_creation_tstmpl;
local_last_changed_by : abp_locinst_lastchange_user;
local_last_changed_at : abp_locinst_lastchange_tstmpl;
last_changed_at : abp_lastchange_tstmpl;
}
Three things to notice:
- Technical key
book_uuid: RAP can generate it on its own when a book is created (managed numbering). The number the user sees isbook_id, the semantic key. @Semantics.amount.currencyCode: everyCURRfield needs its reference currency field.- The five administrative fields (
local_created_by…last_changed_at) use theabp_*data elements. RAP fills them automatically and needs them for optimistic locking (ETag) and, later on, for draft mode. Add them from day one.
2. Test data
A console class to fill the tables. Run it with F9 whenever you want to start over:
CLASS zcl_lib_fill_data DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION.
INTERFACES if_oo_adt_classrun.
ENDCLASS.
CLASS zcl_lib_fill_data IMPLEMENTATION.
METHOD if_oo_adt_classrun~main.
DATA authors TYPE STANDARD TABLE OF zlib_author WITH EMPTY KEY.
DATA books TYPE STANDARD TABLE OF zlib_book WITH EMPTY KEY.
DELETE FROM zlib_book.
DELETE FROM zlib_author.
authors = VALUE #(
( author_id = '000001' name = 'Ana Rojas' country = 'CL' )
( author_id = '000002' name = 'Carlos Soto' country = 'AR' )
( author_id = '000003' name = 'Elena Vidal' country = 'ES' ) ).
books = VALUE #(
( book_id = '00000010' title = 'Birds of the South' author_id = '000001' pages = 320
isbn = '978-0-0000-0010-1' rating = 4 price = '24.90' currency_code = 'USD' )
( book_id = '00000011' title = 'Mountain Rivers' author_id = '000001' pages = 180
isbn = '978-0-0000-0011-1' rating = 5 price = '18.50' currency_code = 'USD' )
( book_id = '00000012' title = 'The Last Train' author_id = '000002' pages = 410
isbn = '978-0-0000-0012-1' rating = 3 price = '21.00' currency_code = 'USD' )
( book_id = '00000013' title = 'Winter Tales' author_id = '000003' pages = 120
isbn = '978-0-0000-0013-1' rating = 4 price = '12.00' currency_code = 'EUR' ) ).
LOOP AT books ASSIGNING FIELD-SYMBOL(<book>).
TRY.
<book>-book_uuid = cl_system_uuid=>create_uuid_x16_static( ).
CATCH cx_uuid_error.
out->write( 'Could not generate the UUID' ).
RETURN.
ENDTRY.
ENDLOOP.
INSERT zlib_author FROM TABLE @authors.
INSERT zlib_book FROM TABLE @books.
out->write( |{ lines( authors ) } authors and { lines( books ) } books loaded| ).
ENDMETHOD.
ENDCLASS.
It is the same VALUE #( … ) syntax from lesson 1, now typed against the real tables. The client field is left out: the system sets it.
3. The author view
A simple CDS View Entity on top of zlib_author. It will be the target of an association:
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Library: authors'
define view entity ZI_LibAuthor
as select from zlib_author
{
key author_id as AuthorId,
name as Name,
country as Country
}
Field names go from snake_case (table) to CamelCase (CDS). That is the convention you will see across RAP code.
4. The book Root View Entity
The root entity is the head of the business object: everything else (behavior, projection, service) hangs from it. By convention it carries the R_ prefix; the projection will use C_ and a reusable interface, I_.
@AccessControl.authorizationCheck: #NOT_REQUIRED
@Metadata.allowExtensions: true
@EndUserText.label: 'Library: books'
define root view entity ZR_LibBook
as select from zlib_book
association [0..1] to ZI_LibAuthor as _Author
on $projection.AuthorId = _Author.AuthorId
association [0..1] to I_Currency as _Currency
on $projection.CurrencyCode = _Currency.Currency
{
key book_uuid as BookUuid,
book_id as BookId,
title as Title,
author_id as AuthorId,
pages as Pages,
isbn as Isbn,
rating as Rating,
@Semantics.amount.currencyCode: 'CurrencyCode'
price as Price,
@Consumption.valueHelpDefinition: [ {
entity.name: 'I_CurrencyStdVH',
entity.element: 'Currency',
useForValidation: true
} ]
currency_code as CurrencyCode,
@Semantics.user.createdBy: true
local_created_by as LocalCreatedBy,
@Semantics.systemDateTime.createdAt: true
local_created_at as LocalCreatedAt,
@Semantics.user.localInstanceLastChangedBy: true
local_last_changed_by as LocalLastChangedBy,
@Semantics.systemDateTime.localInstanceLastChangedAt: true
local_last_changed_at as LocalLastChangedAt,
@Semantics.systemDateTime.lastChangedAt: true
last_changed_at as LastChangedAt,
// Public associations
_Author,
_Currency
}
Associations
association [0..1] to ZI_LibAuthor as _Author on …declares the relationship: each book has at most one author. The_prefix is a convention.$projection.AuthorIdrefers to the field as named in this view (AuthorId), not in the table.- An association is lazy: no JOIN happens until someone uses it (for example,
_Author.Name).
Typical mistake: declaring the association and forgetting to list it at the end (
_Author, _Currency). The association exists, but nobody outside the view can use it.
Value help on the currency
@Consumption.valueHelpDefinition connects the field to I_CurrencyStdVH, a standard value help view. With useForValidation: true, the Fiori UI will also reject currencies that do not exist.
Administrative @Semantics annotations
They tell RAP which field is “created by”, “changed at” and so on. With them, the framework fills those fields automatically once we add behavior.
5. Check the result
Activate everything (Ctrl+Shift+F3), open ZR_LibBook and press F8 (Data Preview):
- you should see the four books;
- right-click a row → Follow Association →
_Authorto jump to the book’s author.
If you see the data and the navigation works, the data model is ready. In the next lesson we expose it as an OData service and open it in a Fiori app without writing a single line of UI5.
Get new tutorials by email
One email when something new is published. No spam; unsubscribe anytime.
I will email you to confirm your subscription. Data handled under the privacy policy.