Technology Blogs by SAP
Learn how to extend and personalize SAP applications. Follow the SAP technology blog for insights into SAP BTP, ABAP, SAP Analytics Cloud, SAP HANA, and more.
cancel
Showing results for 
Search instead for 
Did you mean: 
bruce_wang3
Employee
Employee
5,894

Setting Up Local Development


Install Java & Maven & Node.js


Install the cds-dk


npm i -g @sap/cds-dk

Install the VSCode & Plugin


VSCode Pluging : Java Extension Pack

VSCode Pluging : Language support for Java ™ for Visual Studio Code

VSCode Pluging: SAP Cloud Platform core data services plug-in for Visual Studio Code

VSCode Pluging: vscode-sqlite

VSCode Pluging: Debugger for Java

Verify the Setup


java --version
mvn -version
node -version

 

Create First Cap Application


Step1: Generate initial java project


Enter your java project workspace in terminal, and run below command to create initial java project
mvn -B archetype:generate \
-DarchetypeArtifactId=cds-services-archetype \
-DarchetypeGroupId=com.sap.cds \
-DarchetypeVersion=RELEASE \
-DgroupId=com.sap.cap \
-DartifactId=products-service \
-Dpackage=com.sap.cap.productsservice

After run success, the initial project will be generated in your folder. Regarding the case, the folder named products-service



Step2: Check project in VSCode


Drag the project folder into VSCode, the project structure looks like below picture




  • The project is named products-service

  • The db folder stores database-related artifacts

  • The srv folder stores your Java application


 

Step3: Create first CDS service



  1. In srv folder root, create a file, named as admin-service.cds

  2. Add following service definition to the file.


It means define a service AdminService contains domain model Products

service AdminService {
entity Products {
key ID : Integer;
title : String(111);
descr : String(1111);
}
}

Step4: Compile model definition


In VSCode, open a terminal window and run below command to compile project
mvn clean install

After running this command, some files are generated and added to the srv/src/main/resources/edmx folder. This is the default path, where CAP Java runtime looks for the model definitions.

Step5: Run application locally


The created project is based on spring boot, then you could run the application with below command in terminal.

After run success, the application will be started at default port 8080
mvn clean spring-boot:run



Step6: Inspect running result


Open the link in chrome: http://localhost:8080




Create java class for custom event handler



  • Create the Java package, by creating a new folder called handlers under srv/src/main/java/com/sap/cap/productsservice


 

  • Create the Java class file AdminService.java in the created handlers folder, with the following content


 package com.sap.cap.productsservice.handlers;

import java.util.HashMap;
import java.util.Map;

import org.springframework.stereotype.Component;

import com.sap.cds.services.cds.CdsCreateEventContext;
import com.sap.cds.services.cds.CdsReadEventContext;
import com.sap.cds.services.cds.CdsService;
import com.sap.cds.services.handler.EventHandler;
import com.sap.cds.services.handler.annotations.On;
import com.sap.cds.services.handler.annotations.ServiceName;

@Component
@ServiceName("AdminService")
public class AdminService implements EventHandler {

private Map<Object, Map<String, Object>> products = new HashMap<>();

@On(event = CdsService.EVENT_CREATE, entity = "AdminService.Products")
public void onCreate(CdsCreateEventContext context) {
context.getCqn().entries().forEach(e -> products.put(e.get("ID"), e));
context.setResult(context.getCqn().entries());
}

@On(event = CdsService.EVENT_READ, entity = "AdminService.Products")
public void onRead(CdsReadEventContext context) {
context.setResult(products.values());
}

}

This class now handles the READ and CREATE events that target the Products entity of the AdminService.

  • The READ operation just returns all entities kept in memory.

  • The CREATE event extracts the payload from the CQN representation and stores it in memory.


CDS Query Notation (CQN) is the common language in CAP to run queries against services. It can be used to talk to the services defined by your model, but also remote services, such as the database.

The event handler uses the following APIs, which are available for service providers in CAP Java:

  • Event handler classes have to implement the marker interface EventHandler and register themselves as Spring Beans (@Component). The marker interface is important, because it enables the CAP Java runtime to identify these classes among all Spring Beans.

  • Event handler methods are registered with @On@Before, or @After annotations. Every event, such as an entity creation, runs through these three phases. Each phase has a slightly different semantic. You’ll learn more about these semantics in the subsequent tutorial.

  • The annotation @ServiceName specifies the default service name all event handler methods apply to. Here this is AdminService, as this was also the name when defining the service in the CDS model.

  • Event handler methods get an event-specific event context parameter, which provides access to the input parameters of the event and the ability to set the result. For example, let’s look at the CdsCreateEventContext context parameter. The event we’re extending is the CREATE event. The type of the context variable is specific to this extended CREATE event. The onCreate method returns void, as the result is set by running: context.setResult(…).


Restart the application


Before running the command, make sure the application already stopped, otherwise there is an error of port 8080 was used
mvn clean spring-boot: run

Test create product


open a new terminal to run curl like below
curl -X POST http://localhost:8080/odata/v4/AdminService/Products \
-H "Content-Type: application/json" \
-d '{"ID": 42, "title": "My Tutorial Product", "descr": "You are doing an awesome job!"}'

Read data from products entity page




Debug in VSCode


We also could start the application with vscode

  1. stop the running application first with ctrl+c

  2. select the run in vscode to create a vscode running configuration

  3. You could click the run button to run application, then you could set break point in your java code





Change the project as a Reusable Service


Step1: Define the domain model


Previously, we defined a service which contains a cds entity. Normally when modeling with CDS the best practice is to separate services from the domain model

Therefore, we will define the complete domain model in the db folder

  1. Go to your db folder and create a file named schema.cds

  2. Add the following code to the file


    namespace sap.capire.products;

using { Currency, cuid, managed, sap.common.CodeList } from '@sap/cds/common';

entity Products : cuid, managed {
title : localized String(111);
descr : localized String(1111);
stock : Integer;
price : Decimal(9,2);
currency : Currency;
category : Association to Categories;
}

entity Categories : CodeList {
key ID : Integer;
parent : Association to Categories;
children : Composition of many Categories on children.parent = $self;
}

Step2: Understand keywords


The file means to create two domain model: Products, Categories with CDS Definition Language (CDL)

It also imports various common definitions from the @Sisn/cds/common package (a globally available reuse package):

  • Currency

  • cuid

  • managed

  • CodeList


The localized Keyword


The localized keyword can be used to mark elements, which require translation. The ability to store translations for different languages and to store a default fallback translation is automatically handled by CDS for you. You will see this in action in more detail in the next tutorial.

Associations and Compositions


Associations and compositions can be used to define relationships between entities. They often allow you to define these relationships without explicitly working with foreign keys.

While associations define a rather loose coupling between the entities, compositions define a containment relationship. Compositions can also be thought of as defining deep structures. You can perform deep inserts and upserts along these structures.

In your domain model, the Categories entities define a parent and children element. This enables a hierarchy of categories. The children of a category are modelled as a composition. A category with all of its children defines a deep nested structure. Deleting a category would automatically delete all of its children. However, the parent of a category is modelled as an association. Deleting a category obviously shouldn’t delete its parent.

The cuid and managed Aspects


Both cuid and managed are aspects. Aspects extend an entity with additional elements. The cuid aspect adds a key element ID of type UUID to the entity.

The managed aspect adds four additional elements to the entity. These capture the time of the creation and last update of the entity, and the user, which performed the creation and last update.

The CodeList Aspect and the Currency Type


CodeLists can be used to store global, translatable definitions based on codes, such as currencies, countries, or languages. Especially for UIs, a CodeList can be useful to provide a value help for certain input fields.

The Currency definition is a type. It defines an association to a Currencies entity. The Currencies entity is based on ISO 4217 and uses three-letter alpha codes as keys such as EUR or USD and provides the possibility to store the corresponding currency symbol such as  or $.

Step3: Rewrite the AdminService


Previously we defined a simple service, called AdminService, which directly defined the entity Products. As you now have defined the Products entity in your domain model, the AdminService just needs to expose it. In addition, you defined the Categories entity, which should also be part of your service.

  1. Go to srv folder  and open the admin-service.cds file

  2. Replace the content with the following code


using { sap.capire.products as db } from '../db/schema';

service AdminService {
entity Products as projection on db.Products;
entity Categories as projection on db.Categories;
}

Step4: Deploy domain model to sqlite


First, install SQLite to project, in VSCode open a terminal to run below command
npm install --save-dev sqlite3

To initialize the database with the defined domain model, execute the following command in the terminal
cds deploy --to sqlite

After run successfully, there is a sqlite.db file generated in your project folder

Step5: Connect to sqlite within VSCode


open VSCode with your keyborad F1, select SQLite: open database, next select your project sqlite db


check deployed content in sqlitedb



Step5: Configure CAP application to use SQLite database


Go to srv/src/main/resources, locate, and open the application.yaml file. This file was created when you initialized the application.

At the end of the file, add below content, replace /Users/{youruser}/Documents/workspace to your actual folder path
---
spring:
profiles: sqlite
datasource:
url: "jdbc:sqlite:/Users/{youruser}/Documents/workspace/products-service/sqlite.db"
driver-class-name: org.sqlite.JDBC
initialization-mode: never
hikari:
maximum-pool-size: 1

Step6: Use CAP's generic persistence handling


The CAP Java SDK has a Persistence Service that provides out-of-the-box capabilities to store and retrieve entities from a database. Therefore, no custom coding is required for this. The entities defined in your AdminService will be automatically served via OData and you can just delete the AdminService.java file that was created earlier.

Delete the AdminService.java file in the handlers folder.

Step 7: Run and test your application


Run with memory database with below command
mvn spring-boot:run

Run with SQLite database with below command
mvn spring-boot:run -Dspring-boot.run.profiles=sqlite

In terminal, run below curl to insert categories
curl -X POST http://localhost:8080/odata/v4/AdminService/Categories \
-H "Content-Type: application/json" \
-d '{"ID": 1, "name": "TechEd", "descr": "TechEd related topics", "children": [{"ID": 10, "name": "CAP Java", "descr": "Run on Java"}, {"ID": 11, "name": "CAP Node.js", "descr": "Run on Node.js"}]}'

Check inserted data via browser with the URL:

http://localhost:8080/odata/v4/AdminService/Categories



Step 7: Set up for reuse


The reuse of models can be achieved by publishing NPM modules with the models and defining dependencies to these NPM modules in other applications. There are a two steps we need to perform to prepare the products-service application for reuse.

  • The name of the products-service reuse module, should be @Sisn/capire-products




  • To make it easier to reuse the module, an index.cds file can be added to the products-service. This ensures a better decoupling from other applications.

    Create a new file index.cds in the ~/projects/products-service folder and place the following content inside this file


using from './db/schema';
using from './srv/admin-service';

 
3 Comments
eddie_zhang2
Associate
Associate
0 Kudos
Thanks a lot. Bruce. It helps a lot.
0 Kudos
Thanks for nice blog.

 

Just in case you run into this error:
mvn spring-boot:run -Dspring-boot.run.profiles=sqlite
...
[ERROR] Unknown lifecycle phase ".run.profiles=sqlite". You must specify a valid lifecycle phase or a goal in the format <plugin-prefix>:<goal> or <plugin-group-id>:<plugin-artifact-id>[:<plugin-version>]:<goal>. Available lifecycle phases are: validate, initialize, generate-sources, process-sources, generate-resources, process-resources, compile, process-classes, generate-test-sources, process-test-sources, generate-test-resources, process-test-resources, test-compile, process-test-classes, test, prepare-package, package, pre-integration-test, integration-test, post-integration-test, verify, install, deploy, pre-clean, clean, post-clean, pre-site, site, post-site, site-deploy. -> [Help 1]

 

The solution is documented here:

https://stackoverflow.com/questions/60758633/unable-to-set-command-line-profile-in-spring-boot-2-2
https://stackoverflow.com/questions/18697461/unknown-lifecycle-phase-maven/54803858#54803858

This worked for me:
mvn spring-boot:run -D"spring-boot.run.profiles"=sqlite
kadarisreeja
Explorer
0 Kudos
Hi,

I am getting the below error:

"Failed to execute goal com.sap.cds:cds-maven-plugin:1.13.0:cds"

Can you please help me.

 

Regards,

Sreeja.