Search

Consuming Services

A CAP application is not limited to provision a single isolated CDS service but is rather part of a network of interrelated services. To consume the other services the Service Consumption API is used, which allows to interact with any CDS Service registered with this CAP application. It offers run methods to execute CDS QL statements such as query, insert, update, upsert and delete statements that will return a Result data structure.

Of special importance is the Persistence Service that gives access to the underlying CDS Data Store.

Example

A typical usage of the consumption API would be

import com.sap.cds.Result;
import com.sap.cds.ql.Select;
import com.sap.cds.services.ServiceException;
import com.sap.cds.services.persistence.PersistenceService;

// obtain instance of Persistence Service
@Autowired
PersistenceService db;

@Before(event = CdsService.EVENT_CREATE, entity = "OrdersService.OrderItems")
public void validateBookAndDecreaseStock(List<OrderItems> items) {
    OrderItems item = items.get(1);
    String bookId = item.getBookId();

    // compose query check if the book that should be ordered is existing
    CqnSelect sel = Select.from(Books_.class)
                          .columns(b -> b.stock())
                          .where(b -> b.ID().eq(bookId));

    // execute the query
    Result result = db.run(sel);

    // process the result
    if (!result.first().isPresent()) {
        // handle error
        throw new new ServiceException(ErrorStatuses.NOT_FOUND, "Book does not exist")
    }
}

Service Lookup

The application interacts with a CDS Service via a CdsService instance, which can be either looked up from the Service Catalog or, in Spring, obtained via dependency injection.

Getting a Service from the Service Catalog

The ServiceCatalog is accessible from the EventContext and allows to look up service instances:

import com.sap.cds.services.EventContext;
import com.sap.cds.services.ServiceCatalog;
import com.sap.cds.services.handler.EventHandler;

@On(event = "READ", entity = "my.catalogservice.books")
public void readBooksVerify(EventContext context) {
    ServiceCatalog catalog = context.getServiceCatalog();
    CdsService adminService = catalog.getService(CdsService.class, "AdminService");
   [...]
}

Obtaining a Service by Dependency Injection

In Spring, the service instances are registered as Spring Beans and can be obtained by dependency injection:

import com.sap.cds.services.cds.CdsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier
import org.springframework.stereotype.Component;

@Component
public class MyBean {
    @Autowired()
    @Qualifier("AdminService")
    CdsService adminService;
}

Query Execution

CDS QL statements can be executed using the run method of the CdsService:

CdsService service = ...

CqnSelect query = Select.from("my.bookshop.Books")
    .columns("title", "price");

Result result = service.run(query);

Parameterized Execution

Queries as well as update and delete statements can be parameterized with positional or named parameters.

Positional Parameters

The following query uses two positional parameters defined via param():

import static com.sap.cds.ql.CqnBuilder.param;

CqnSelect query = Select.from("my.bookshop.Books")
    .where(b -> b.get("ID").eq(param())
            .or(b.get("ID").eq(param())));

Result result = service.run(query, 101, 102);

Before the execution of the statement the values 101 and 102 are bound to the defined parameters.

Named Parameters

The following query uses two parameters named “id1” and “id2”. The parameter values are given as a map:

import static com.sap.cds.ql.CqnBuilder.param;

CqnSelect query = Select.from("my.bookshop.Books")
    .where(b -> b.get("ID").eq(param("id1"))
            .or(e.get("ID").eq(param("id2"))));

Map paramValues = new HashMap<>();
paramValues.put("id1", 101);
paramValues.put("id2", 102);

Result result = service.run(query, paramValues);

Data Manipulation

The CDS Service API allows to manipulate data by executing insert, update, delete, or upsert statements.

Update

The update operation can be executed as follows.

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

CqnUpdate update = Update.entity("my.bookshop.Books").data(book).where(b -> b.get("ID").eq(101));
long updateCount = service.run(update).rowCount();

Working with Structured Documents

It’s possible to work with structured data as the insert, upsert, and delete operations are cascading along compositions. The update operation, however, doesn’t cascade.

Deep Insert / Upsert

Insert and upsert statements for an entity have to include the keys and optionally data for the entity’s composition targets, which are then inserted resp. upserted along with the root entity. A deep upsert is equivalent to a cascading delete followed by a deep insert.

Iterable<Map<String, Object>> books;

CqnInsert insert = Insert.into("my.bookshop.Books").entries(books);
Result result = service.run(insert);

CqnUpsert upsert = Upsert.into("my.bookshop.Books").entries(books);
Result result = service.run(upsert);

Cascading Delete

The delete operation is cascaded along the entity’s compositions. All composition targets that are reachable from the (to be deleted) entity are deleted as well.

The following example deletes the order with id 1000 including all its items.

CqnDelete delete = Delete.from("my.bookshop.Orders").matching(singletonMap("OrderNo", 1000));
long deleteCount = service.run(delete).rowCount();

The Persistence Service

The domain model of a CAP application can be mapped to a database. Typically, an SAP HANA database is used in production. For test and development, it’s also possible to use a light-weight, in-memory database such as SQLite.

The relational data in the database is exposed by the Persistence Service. CAP automatically handles the events of the CDS Services by delegating them to the downstream Persistence Service. The Persistence Service eventually executes the required operations on the database via the CDS Data Store.

The Persistence Service is not bound to a particular service definition but gives access to all entities of the CDS model of the CAP application, which are mapped to the database. Just as with a CDS Service it is possible to register event handlers for particular events of the Persistence Service.

The PersistenceService instance can be looked up in the service catalog

import com.sap.cds.services.EventContext;
import com.sap.cds.services.ServiceCatalog;
import com.sap.cds.services.handler.EventHandler;
import com.sap.cds.services.persistence.PersistenceService;

@On(event = "READ", entity = "my.catalogservice.books")
public void readBooksVerify(EventContext context) {
    ServiceCatalog catalog = context.getServiceCatalog();
    PersistenceService db = catalog.getService(PersistenceService.class, PersistenceService.DEFAULT_NAME);
   [...]
}

or, in Spring, be injected.