Generate a typed OData client for Java

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.

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.

note

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.

tip

The OData VDM generator does support all data types as 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:

<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>
Lombok and Dependency injection are mandatory!

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 Getters/Setters

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:

<plugin>
<groupId>com.sap.cloud.sdk.datamodel</groupId>
<artifactId>odata-v4-generator-maven-plugin</artifactId>
<!-- Please use the latest version here-->
<version>3.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>
</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. Also the <compileScope> option instructs maven to add the generated code as sources for the compile phase.

caution

In most cases 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 service name. The URL path will be build 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 CLI#

  1. 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.
  1. 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.

  2. 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.

caution

In most cases 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 service name. The URL path will be build 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.

Invoke the generator programmatically#

  1. Please include the odata-v4-generator artifact as a dependency in a your project. Choose a module and location from which you intend to invoke the generator and add the following dependency to the appropriate pom.xml.
    <dependency>
    <groupId>com.sap.cloud.sdk.datamodel</groupId>
    <artifactId>odata-v4-generator</artifactId>
    </dependency>
  1. 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(DefaultNamingStrategy.NameSource.NAME)
    .withPackageName("org.example")
    .withDefaultBasePath("/my/path/")
    .execute();
  2. 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 setup correctly and that the service name mappings meet your expectations.

caution

In most cases 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 service name. The URL path will be build 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:

ParameterDefaultDescription
<compileScope>NONEAdds the generated sources to the compile or test phase
<deleteOutputDirectory>FalseTarget 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>inputLocation of the metadata files
<namingStrategy>- Fully-qualified Java class to be used as the naming strategy
<outputDirectory>outputOutput directory for generated sources
<overwriteFiles>FalseOverwrite existing files
<packageName>- Package name for the generated sources
<serviceNameMappingFile>- Determine service names from a given mapping file

More information is also available in the Javadoc of the generator implementation (OData v2, OData v4).

Last updated on by Matthias Kuhr