← Volver a tutoriales

RAP desde cero · Lección 2 de 6

Modelo de datos en RAP: tablas, CDS View Entity y asociaciones

Crea las tablas de una biblioteca, carga datos de prueba y define la Root View Entity con sus asociaciones: la base de cualquier objeto de negocio RAP.

Also available in English →

Toda aplicación RAP se construye por capas, de abajo hacia arriba:

  1. Datos: tablas de base de datos y CDS View Entities (esta lección).
  2. Proyección y servicio: lo que se expone hacia fuera (lección 3).
  3. Comportamiento: qué se puede hacer con los datos y con qué reglas (lección 4).

Seguimos con la biblioteca de la lección anterior: autores y libros.

1. Las tablas

En ADT: New → Other ABAP Repository Object → Dictionary → Database Table.

Autores, una tabla sencilla de consulta:

@EndUserText.label : 'Biblioteca: autores'
@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);
}

Libros, que será el objeto de negocio RAP:

@EndUserText.label : 'Biblioteca: libros'
@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;
}

Fíjate en tres cosas:

  • Clave técnica book_uuid: RAP la podrá generar sola al crear un libro (managed numbering). El número visible para el usuario es book_id, la clave semántica.
  • @Semantics.amount.currencyCode: todo campo CURR necesita su campo de moneda de referencia.
  • Los cinco campos de administración (local_created_by … last_changed_at) usan los elementos de datos abp_*. RAP los rellena solo y los necesitará para el bloqueo optimista (ETag) y, más adelante, para el modo draft. Ponlos desde el principio.

2. Datos de prueba

Una clase de consola para llenar las tablas. Ejecútala con F9 cada vez que quieras volver a empezar:

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 = 'Aves del sur'        author_id = '000001' pages = 320
        isbn = '978-0-0000-0010-1' rating = 4 price = '24.90' currency_code = 'USD' )
      ( book_id = '00000011' title = 'Ríos de montaña'     author_id = '000001' pages = 180
        isbn = '978-0-0000-0011-1' rating = 5 price = '18.50' currency_code = 'USD' )
      ( book_id = '00000012' title = 'El último tren'      author_id = '000002' pages = 410
        isbn = '978-0-0000-0012-1' rating = 3 price = '21.00' currency_code = 'USD' )
      ( book_id = '00000013' title = 'Cuentos de invierno' 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( 'No se pudo generar el UUID' ).
          RETURN.
      ENDTRY.
    ENDLOOP.

    INSERT zlib_author FROM TABLE @authors.
    INSERT zlib_book FROM TABLE @books.

    out->write( |{ lines( authors ) } autores y { lines( books ) } libros cargados| ).
  ENDMETHOD.
ENDCLASS.

Es la misma sintaxis VALUE #( … ) de la lección 1, ahora con los tipos de las tablas reales. El campo client no se informa: el sistema lo pone solo.

3. La vista de autores

Una CDS View Entity sencilla sobre zlib_author. Será el destino de una asociación:

@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Biblioteca: autores'
define view entity ZI_LibAuthor
  as select from zlib_author
{
  key author_id as AuthorId,
      name      as Name,
      country   as Country
}

Los nombres de los campos pasan de snake_case (tabla) a CamelCase (CDS). Es la convención que verás en todo el código RAP.

4. La Root View Entity del libro

La entidad raíz es la cabeza del objeto de negocio: todo lo demás (comportamiento, proyección, servicio) cuelga de ella. Por convención lleva el prefijo R_; la proyección llevará C_ y una interfaz reutilizable, I_.

@AccessControl.authorizationCheck: #NOT_REQUIRED
@Metadata.allowExtensions: true
@EndUserText.label: 'Biblioteca: libros'
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,

      // Asociaciones públicas
      _Author,
      _Currency
}

Las asociaciones

  • association [0..1] to ZI_LibAuthor as _Author on … declara la relación: cada libro tiene como mucho un autor. El prefijo _ es convención.
  • $projection.AuthorId se refiere al campo tal como se llama en esta vista (AuthorId), no en la tabla.
  • Una asociación es perezosa: no se hace ningún JOIN hasta que alguien la usa (por ejemplo, _Author.Name).

Error típico: declarar la asociación y olvidar listarla al final (_Author, _Currency). La asociación existe, pero nadie puede usarla desde fuera de la vista.

Ayuda de búsqueda en la moneda

@Consumption.valueHelpDefinition conecta el campo con I_CurrencyStdVH, una vista estándar de ayuda de búsqueda. Con useForValidation: true, la interfaz Fiori también rechazará monedas que no existan.

Las anotaciones @Semantics de administración

Le dicen a RAP qué campo es «creado por», «modificado el», etc. Con ellas, el framework los rellenará automáticamente cuando añadamos el comportamiento.

5. Comprueba el resultado

Activa todo (Ctrl+Shift+F3), abre ZR_LibBook y pulsa F8 (Data Preview):

  • deberías ver los cuatro libros;
  • con clic derecho en una fila → Follow Association → _Author, navegas al autor del libro.

Si ves los datos y la navegación funciona, el modelo de datos está listo. En la próxima lección lo exponemos como servicio OData y lo vemos en una aplicación Fiori sin escribir una línea de UI5.