Generate a Typed OData Client With the OData Generator
The OData Generator allows for generating Java classes from the metadata of an OData service. These classes which are referred to as typed OData client provide type-safe access to the service.
The generator is designed to generate source code in english. You may also generate a client based on other languages in the EDMX file. However, languages that use non-latin characters, specifically languages that read from right to left or that don't have capitalisation, may not be supported.
In general, there are three ways to use the generator:
- Via the dedicated maven plugin
- Via the command-line interface (CLI)
- By instantiating and invoking it at runtime
The maven plugin is usually the recommended way as it integrates nicely with most project setups and makes configuration easy. However, the other two approaches are available and all are documented below.
For all three the required input is an EDMX
file holding the service metadata.
Please be aware that OData v2 and OData v4 service definitions are not interchangeable. There is a dedicated generator for each protocol version and it only accepts service definitions for that version.
The OData VDM generator does support all data types per the OData specification. If your OData service metadata uses a data type not listed in the standard, please alter your metadata to use a compatible data type from the specification instead.
For example, a non-standard Edm.Float
type can be substituted by Edm.Single
type from the OData specification.
Using the OData Generator
Regardless of how the generator is invoked the generated code requires some dependencies to be present. Therefore, it is required to ensure the following dependencies are present in your project:
- OData v2
- OData v4
<dependency>
<groupId>com.sap.cloud.sdk.datamodel</groupId>
<artifactId>odata-v4-core</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.sap.cloud.sdk.datamodel</groupId>
<artifactId>odata-core</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
<scope>provided</scope>
</dependency>
Lombok and dependency injections are used by the generated typed OData client classes, that is why they are needed but only with the scope provided. Furthermore, some common IDEs (e.g. IntelliJ, Eclipse) require plugins to recognize these annotations. See the note on missing accessors
Using the OData Generator Maven Plugin
The maven plugin is most useful if you would like to use the generated code within a maven project.
Use it by adding it to your application/pom.xml
file under the <plugin>
section:
- OData v2
- OData v4
<plugin>
<groupId>com.sap.cloud.sdk.datamodel</groupId>
<artifactId>odata-v4-generator-maven-plugin</artifactId>
<!-- Please use the latest version here-->
<version>4.XX.X</version>
<executions>
<execution>
<id>generate-consumption</id>
<phase>generate-sources</phase>
<goals>
<goal>generate</goal>
</goals>
<configuration>
<inputDirectory>${project.basedir}/edmx</inputDirectory>
<outputDirectory>${project.build.directory}/vdm</outputDirectory>
<deleteOutputDirectory>true</deleteOutputDirectory>
<packageName>com.mycompany.vdm</packageName>
<defaultBasePath>odata/v4/</defaultBasePath>
<compileScope>COMPILE</compileScope>
<serviceMethodsPerEntitySet>true</serviceMethodsPerEntitySet>
<!-- (Optional) You can add a custom copyright header:
<copyrightHeader>Copyright (c) this year, my company</copyrightHeader>
Or use the SAP copyright header:
<sapCopyrightHeader>true</sapCopyrightHeader>
-->
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>com.sap.cloud.sdk.datamodel</groupId>
<artifactId>odata-generator-maven-plugin</artifactId>
<!-- Please use the latest version here-->
<version>4.XX.X</version>
<executions>
<execution>
<id>generate-consumption</id>
<phase>generate-sources</phase>
<goals>
<goal>generate</goal>
</goals>
<configuration>
<inputDirectory>${project.basedir}/edmx</inputDirectory>
<outputDirectory>${project.build.directory}/vdm</outputDirectory>
<deleteOutputDirectory>true</deleteOutputDirectory>
<packageName>com.mycompany.vdm</packageName>
<defaultBasePath>odata/v2/</defaultBasePath>
<compileScope>COMPILE</compileScope>
<serviceMethodsPerEntitySet>true</serviceMethodsPerEntitySet>
<!-- (Optional) You can add a custom copyright header:
<copyrightHeader>Copyright (c) this year, my company</copyrightHeader>
Or use the SAP copyright header:
<sapCopyrightHeader>true</sapCopyrightHeader>
-->
</configuration>
</execution>
</executions>
</plugin>
Adapt the <inputDirectory>
to point to the location of your service definitions.
A full list of parameters is available here.
Now maven will run the generator within the process-sources
phase which is executed before compile
.
The <compileScope>
option instructs maven to add the generated code as sources for the compile phase.
Usually, you will have to override the generated service path in the code.
By default, the generator will use the namespace
value of the <schema>
tag as the service name.
The URL path will be built as default-base-path
+ service-name
.
If your service definition does not contain this value or the value does not match what is needed in the URL then you need to override the service path in the code.
Use the .withServicePath("/path/to/my/service")
option on the service class.
Using the Plugin from the Command Line
The maven plugin can also be invoked without a project from the command line using -D
parameter flags, for example:
mvn com.sap.cloud.sdk.datamodel:odata-v4-generator-maven-plugin:4.XX.X:generate -Dodatav4.generate.inputDirectory=foo -Dodatav4.generate.outputDirectory=bar
See the full list of parameters here.
Using the CLI
- OData v2
- OData v4
- Download the latest command-line interface (CLI) of the generator from Maven Central.
Rename it to
odata-generator-cli.jar
and put it in a directory of your choice.
- Download the latest command-line interface (CLI) of the generator from maven central.
Rename it to
odata-generator-cli.jar
and put it in a directory of your choice.
-
Run
java -jar odata-generator-cli.jar -i /path/to/input/folder -o /path/to/output/folder
. You can also specify the parameter-p "my.package.name"
to choose the package name and-b "/my/path"
to choose the base path. A full list of parameters is available here. -
Put the generated Java source files from the output folder into your project that is using the SAP Cloud SDK so that they are picked up by Java. For example, move them to the
application/src/main/java
folder.
Usually, you will have to override the generated service path in the code.
By default, the generator will use the namespace
value of the <schema>
tag as the service name.
The URL path will be built as default-base-path
+ service-name
.
If your service definition does not contain this value or the value does not match what is needed in the URL then you need to override the service path in the code.
Use the .withServicePath("/path/to/my/service")
option on the service class.
Generate Clients Programmatically
- OData v2
- OData v4
- Please include the
odata-v4-generator
artifact as a dependency in your project. Choose a module and location from which you intend to invoke the generator and add the following dependency to the appropriatepom.xml
.<dependency>
<groupId>com.sap.cloud.sdk.datamodel</groupId>
<artifactId>odata-v4-generator</artifactId>
</dependency>
- Please include the
odata-generator
artifact as a dependency in your project. Choose a module and location from which you intend to invoke the generator and add the following dependency to the appropriatepom.xml
.<dependency>
<groupId>com.sap.cloud.sdk.datamodel</groupId>
<artifactId>odata-generator</artifactId>
</dependency>
-
Copy the following code which will later invoke the generator:
final Path inputDirectory = Paths.get("application/src/main/resources/");
final Path outputDirectory = Paths.get("application/src/main/java/");
final Path serviceNameMapping = inputDirectory.resolve("serviceNameMappings.properties");
new DataModelGenerator()
.withInputDirectory(inputDirectory.toFile())
.withOutputDirectory(outputDirectory.toFile())
.withServiceNameMapping(serviceNameMapping.toFile())
.pojosOnly(false)
.withNameSource(NameSource.NAME)
.withPackageName("org.example")
.withDefaultBasePath("/my/path/")
.serviceMethodsPerEntitySet()
.execute(); -
Adapt the input & output directory as well as the package name according to your setup. A full list of parameters is available here. Place your EDMX file within the input folder and run the generator.
This should give you the generated classes in the desired folder. You can now proceed with using them to build requests.
In case you run into issues with the above process:
- Double-check your service and file names
- Check that the folders are set up correctly
- The service name mappings meet your expectations
Usually, you will have to override the generated service path in the code.
By default, the generator will use the namespace
value of the <schema>
tag as the service name.
The URL path will be built as default-base-path
+ service-name
.
If your service definition does not contain this value or the value does not match what is needed in the URL then you need to override the service path in the code.
Use the .withServicePath("/path/to/my/service")
option on the service class.
Available Parameters
The following parameters are available on the generator for both OData protocol versions:
- Maven Plugin
- CLI
- Java
Parameter | Default | Description |
---|---|---|
<compileScope> | NONE | Adds the generated sources to the compilation or test phase |
<copyrightHeader> | null | Copyright header to be added at the top of generated files |
<deleteOutputDirectory> | False | Target directory is deleted before code generation |
<defaultBasePath> | - | Base path of the exposed API |
<includeEntitySets> | - | Only generate classes for specific entity sets |
<includeFunctionImports> | - | Only generate classes for specific function imports |
<inputDirectory> | input | Location of the metadata files |
<namingStrategy> | - | Fully-qualified Java class to be used as the naming strategy |
<nameSource> | LABEL | Source for entity and property names (either LABEL or NAME ) |
<outputDirectory> | output | Output directory for generated sources |
<overwriteFiles> | False | Overwrite existing files |
<packageName> | - | Package name for the generated sources |
<sapCopyrightHeader> | False | Add the SAP copyright header (overrides copyrightHeader ) |
<serviceNameMappingFile> | - | Determine service names from a given mapping file |
<serviceMethodsPerEntitySet> | False | Generate service methods per each entity set for one entity type |
<keepExistingSignatures> [^1] | False | Try to avoid API breaking changes by sticking to existing generated method[^2] signatures |
[^1] Feature is in experimental state.
[^2] The keep existing signatures switch only works if the output directory already contains a generated client library.
More information is also available in the Javadoc of the generator implementation (OData v2, OData v4).
Parameter | Alias | Default | Description |
---|---|---|---|
--copyrightHeader <arg> | none | null | Copyright header to be added at the top of generated files |
--default-base-path <arg> | -b | - | Base path of the exposed API |
--delete-output-dir | -d | False | Target directory is deleted before code generation |
--help | -h | - | Print a help message. |
--input-dir <arg> | -i | input | Location of the metadata files |
--include-entity-sets <arg> | -ies | - | Only generate classes for specific entity sets |
--include-function-imports <arg> | -ifn | - | Only generate classes for specific function imports |
--name-mapping-file <arg> | -m | - | Determine service names from a given mapping file |
--name-strategy-class <arg> | -n | - | Fully-qualified Java class to be used as the naming strategy |
--use-odata-names | none | - | Generates entity and property names based on the names within the EDMX file instead of the sap:label |
--output-dir <arg> | -o | output | Output directory for generated sources |
--overwrite-files | -f | False | Overwrite existing files |
--package-name-prefix <arg> | -p | - | Package name for the generated sources |
--sapCopyrightHeader | none | False | Add the SAP copyright header (overrides copyrightHeader ) |
--service-methods-per-entity-set | none | False | Generate service methods per each entity set for one entity type |
For details leverage the --help
option.
More information is also available in the Javadoc of the generator implementation (OData v2, OData v4).
Parameter | Default | Description |
---|---|---|
copyrightHeader(String) | null | Copyright header to be added at the top of generated files |
deleteOutputDirectory() | False | Target directory is deleted before code generation |
overwriteFiles() | False | Overwrite existing files |
withDefaultBasePath(String) | - | Base path of the exposed API |
withIncludedEntitySets(Set<String>) | - | Only generate classes for specific entity sets |
withIncludedFunctionImports(Set<String>) | - | Only generate classes for specific function imports |
withInputDirectory(String) | input | Location of the metadata files |
withNamingStrategy(NamingStrategy) | - | Fully-qualified Java class to be used as the naming strategy |
withNameSource(NameSource) | - | Source for entity and property names (either NameSource.LABEL or NameSource.NAME ) |
withOutputDirectory(String) | output | Output directory for generated sources |
withPackageName(String) | - | Package name for the generated sources |
withServiceNameMapping(String) | - | Determine service names from a given mapping file |
sapCopyrightHeader() | False | Add the SAP copyright header (overrides copyrightHeader ) |
serviceMethodsPerEntitySet() | False | Generate service methods per each entity set for one entity type |
keepExistingSignatures() [^1] | False | Try to avoid API breaking changes by sticking to existing generated[^2] method signatures |
[^1] Feature is in experimental state.
[^2] The keep existing signatures switch only works if the output directory already contains a generated client library.
More details can be found on the Javadoc (OData v2, OData v4).
Available Naming Strategies
The SAP Cloud SDK offers two different naming strategies that can be used out-of-the-box:
com.sap.cloud.sdk.datamodel.odata.utility.S4HanaNamingStrategy
(default)com.sap.cloud.sdk.datamodel.odata.utility.SimpleNamingStrategy
While both strategies, of course, generate syntactically valid Java names, the S4HanaNamingStrategy
tries to make the names as aligned with common Java coding convention as possible.
This is achieved by stripping away common pre- and suffixes used in SAP service specifications, such as the prefix SAP_
or the suffix Type
.
Furthermore, some names are even extended by, for example, adding the prefix to
to indicate a navigation property.
In contrast to that, the SimpleNamingStrategy
performs only the bare minimum of modifications to convert the specified names into valid Java syntax.