Search

Working with Data

This chapter describes how data is represented and used in the CAP Java SDK.

Content

Predefined Types

The predefined CDS types are mapped to Java types and as follows:

CDS Type Java Type Remark
cds.UUID java.lang.String  
cds.Boolean java.lang.Boolean  
cds.Integer java.lang.Integer  
cds.Integer64 java.lang.Long  
cds.Decimal java.math.BigDecimal  
cds.DecimalFloat java.math.BigDecimal deprecated
cds.Double java.lang.Double  
cds.Date java.time.LocalDate date without a time-zone (year-month-day)
cds.Time java.time.LocalTime time without a time-zone (hour-minute-second)
cds.DateTime java.time.Instant instant on the time-line with sec precision
cds.Timestamp java.time.Instant instant on the time-line with µs precision
cds.String java.lang.String  
cds.LargeString java.lang.String java.io.Reader (1) if annotated with @Core.MediaType
cds.Binary byte[]  
cds.LargeBinary byte[] java.io.InputStream (1) if annotated with @Core.MediaType

(1) Although the API to handle large objects is the same for every database, the streaming feature, however, is supported (and tested) in HANA, PostgreSQL, and H2. For more details on database support and limitations, see section Database Support in Java.

Important: The framework is not responsible for closing the stream when writing to the database. You decide when the stream should be closed. If you forget to close the stream, this may lead to a memory leak.

These types are used for the values of CDS elements with primitive type.

In the Model Reflection API, they’re represented by the enum CdsBaseType.

Structured Data

Structured data is used as input for Insert, Update, and Upsert statements and represents the results of Select statements.

In the following we use this CDS model:

entity Books {
    key ID     : Integer;
        title  : String;
        author : Association to one Authors;
}

entity Authors {
    key ID    : Integer;
        name  : String;
        books : Association to many Books on books.author = $self; 
}

Entities and Structured Types

Entities or structured types are represented in Java as a Map<String, Object> that maps the element names to the element values.

The following example shows JSON data and how it can be constructed in Java:

{
    "ID"    : 97,
    "title" : "Dracula"
}
//java
Map<String, Object> book = new HashMap<>();
book.put("ID", 97);
book.put("title", "Dracula");

Data of structured types and entities can be sparsely populated.

Nested Structures and Associations

Nested structures and single-valued associations, are represented by elements where the value is structured. In Java, the value type for such a representation is a map.

The following example shows JSON data and how it can be constructed in Java:

{
    "ID"     : 97,
    "author" : 
        {
            "ID"   : 23,
            "name" : "Bram Stoker"
        }
}
// java
Map<String, Object> author = new HashMap<>();
author.put("ID", 23);
author.put("name", "Bram Stoker");

Map<String, Object> book = new HashMap<>();
book.put("ID", 97);
book.put("author", author);

A to-many association is represented by a List<Map<String, Object>>.

The following example shows JSON data and how it can be constructed in Java:

{
    "ID"    : 23,
    "books" : 
        [
            {
                "ID" : 97,
                "title" : "Dracula"
            },
            {
                "ID"   : 98,
                "name" : "Miss Betty"
            }
        ],
    "name" : "Bram Stoker"
}
// java
Map<String, Object> book1 = new HashMap<>();
book1.put("ID", 97;
book1.put("title", "Dracula");

Map<String, Object> book2 = new HashMap<>();
book2.put("ID", 98);
book2.put("title", "Miss Betty");

Map<String, Object> author = new HashMap<>();
author.put("ID", 23);
author.put(books, Arrays.asList(book1, book2));
author.put("name", "Bram Stoker");

Typed Access

Representing data given as Map<String, Object> is flexible and interoperable with other frameworks. But it also has some disadvantages:

  • Names of elements are checked only at runtime
  • No code completion in the IDE
  • No type safety

To ease the handling of data, the CAP Java SDK additionally provides typed access to data through accessor interfaces:

Let’s assume following data for a Book:

Map<String, Object> book = new HashMap<>();
book.put("ID", 97);
book.put("title", "Dracula");

You can now either define an accessor interface or use a generated accessor interface. The accessor interface then looks like in the following example:

interface Book extends Map<String, Object> {
  @CdsName("ID")   // name of the CDS element
  Integer getID();

  String getTitle();
  void setTitle(String title);
}

At runtime, the Struct.access method is used to create a proxy that gives typed access to the data through the accessor interface:

import static com.sap.cds.Struct.access;
...

Book book = access(data).as(Book.class);

String title = book.getTitle();   // read the value of the element 'title' from the underlying map
book.setTitle("Miss Betty");      // update the element 'title' in the underlying map

title = data.get("title");        // direct access to the underlying map

title = book.get("title");        // hybrid access to the underlying map through the accessor interface

To support hybrid access, like simultaneous typed and generic access, the accessor interface just needs to extend Map<String, Object>.

The name of the CDS element referred to by a getter or setter, is defined through @CdsName annotation. If the annotation is missing, it’s determined by removing the get/set from the method name and lowercasing the first character.

Generated Accessor Interfaces

For all structured types of the CDS model, accessor interfaces can be generated using the CDS4j Maven Plugin. The generated accessor interfaces allow for hybrid access and easy serialization to JSON.

Creating a Data Container for an Interface

To create an empty data container for an interface, use the Struct.create method:

import static com.sap.cds.Struct.create;
...

Book book = create(Book.class);

book.setTitle("Dracula");
String title = book.getTitle();   // title: "Dracula"

Generated accessor interfaces contain a static create method that further facilitates the usage:

Book book = Books.create();

book.setTitle("Dracula");
String title = book.getTitle();   // title: "Dracula"

Read-Only Access

Create a typed read-only view using access. Calling a setter on the view throws an exception.

import static com.sap.cds.Struct.access;
...

Book book = access(data).asReadOnly(Book.class);

String title = book.getTitle();
book.setTitle("CDS4j");           // throws Exception

Typed Streaming of Data

Data given as Iterable<Map<String, Object>> can also be streamed:

import static com.sap.cds.Struct.stream;
...

Stream<Book> books = stream(data).as(Book.class);

List<Book> bookList = books.collect(Collectors.toList());

Typed Access to Query Results

Typed access through custom or generated accessor interfaces eases the processing of query result.