Generating POJOs using OpenAPI generator with Lombok Annotations - java

I am using OpenAPI generator maven plugin like one below for generating Java client code for models .
<plugin>
<groupId>org.openapitools</groupId>
<artifactId>openapi-generator-maven-plugin</artifactId>
<version>4.3.1</version>
<executions>
<execution>
<goals>
<goal>generate</goal>
</goals>
<configuration>
<inputSpec>${project.basedir}/src/main/resources/api.yaml</inputSpec>
<generatorName>java</generatorName>
<configOptions>
<sourceFolder>src/gen/java/main</sourceFolder>
</configOptions>
</configuration>
</execution>
</executions>
</plugin>
When , I generate the model classes, they get generated with usual POJO field declarations and getters and setters. But what I want to do is, instead of generating getters and setters, I want my classes to get automatically generated with Lombok annotations for Java pojos like #Getter, #Setter, #Data, etc. Is there a way to customize model generator to fit above use case requirement?
I tried to find out if there is a way. I found this discussion, where the very last comment talks about a PR, where the issue of generating models using Lombok annotations has been addressed. But I do not see any clear indication of usage or any documentation of this feature in the OpenAPI generator open source project that it has been implemented yet. So, is there any way of generating models with Lombok annotations instead of regular getters and setters today?

To complete this very old thread: Now it does support Lombok annotations.
Example taken from here
<configOptions>
<additionalModelTypeAnnotations>#lombok.Builder #lombok.NoArgsConstructor #lombok.AllArgsConstructor</additionalModelTypeAnnotations>
</configOptions>

EDIT: This answer is deprecated. See the post by #Laess3r. I'll leave this, since it is applicable for older versions of openapi generator.
openapi-generator does not yet support Lombok annotations. If you want to generate code with Lombok annotations, you need to create a custom template in mustache, as described in https://openapi-generator.tech/docs/templating/.
If you've never worked with mustache, be aware that it's somewhat hard to read, so try to keep the templates as simple as possible and make sure to add unit tests to validate the generated output. The template will look something like this:
/**
* {{#description}}{{description}}{{/description}}
*/
#Data
public class {{classname}} {{#parent}}extends {{{parent}}} {{/parent}} {
{{#vars}}
/**
* {{#description}}{{description}}{{/description}}
*/
#JsonProperty("{{#lambda.lowercase}}{{nameInSnakeCase}}{{/lambda.lowercase}}")
private {{{datatypeWithEnum}}} {{name}};
{{/vars}}

I've been able to get this working out-of-the-box using a space
separated list of annotations on models:
#lombok.experimental.SuperBuilder #lombok.external.Jacksonized
If models have readOnly set to "true" the Builder becomes the only way to make the object and #Jacksonized allows it to be serialized/deserialized. There are some limitations with inheritance (turning off requiring all required parameters in the configOptions).

Related

How to generate Swagger codegen Java models as JPA Entities

I am using Swagger codegen to create Java models to be used in a Spring REST server, and would like to know how to get Swagger to declare each model as a JPA entity.
I generate the code with the swagger-codegen-maven-plugin as follows:
<plugin>
<groupId>io.swagger</groupId>
<artifactId>swagger-codegen-maven-plugin</artifactId>
<version>2.4.0</version>
<executions>
<execution>
<goals>
<goal>generate</goal>
</goals>
<configuration>
<inputSpec>${project.basedir}/src/main/openApi/Rack.json</inputSpec>
<language>spring</language>
<groupId>com.me</groupId>
<artifactId>rest-server</artifactId>
<apiPackage>com.me.rest.api</apiPackage>
<modelPackage>com.me.rest.model</modelPackage>
<invokerPackage>com.me.rest.invoker</invokerPackage>
<configOptions>
<sourceFolder>src/gen/java/main</sourceFolder>
<java8>true</java8>
<dateLibrary>java8</dateLibrary>
</configOptions>
</configuration>
</execution>
</executions>
</plugin>
As I have it now, this is the abbreviated java code that gets generated:
#Validated
#javax.annotation.Generated(value = "io.swagger.codegen.languages.SpringCodegen", date = "...")
public class Rack {
#JsonProperty("id")
private Long id = null;
#JsonProperty("name")
private String name = null;
...
}
How do I get Swagger to add the #Entity and #Id JPA annotations, as follows?
import javax.persistence.Entity;
import javax.persistence.Id;
#Entity
#Validated
#javax.annotation.Generated(value = "io.swagger.codegen.languages.SpringCodegen", date = "...")
public class Rack {
#Id
#JsonProperty("id")
private Long id = null;
#JsonProperty("name")
private String name = null;
...
}
This way, all I would have to do to get Spring to automatically expose these generated classes as REST APIs, would be to add the following to my pom.xml:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-rest</artifactId>
</dependency>
Then I could create the JPA repositories with Spring-Data, as follows:
public interface RackRepository extends CrudRepository<Rack, Long> {
}
A PR has recently been merged fixing your issue : https://github.com/OpenAPITools/openapi-generator/pull/11775
You need to upgrade your Maven plugin to use the latest version (currently unreleased, only snapshot is available)
<plugin>
<groupId>org.openapitools</groupId>
<artifactId>openapi-generator-maven-plugin</artifactId>
<version>6.0.0-SNAPSHOT</version>
...
</plugin>
The configuration might be slightly different.
Then you need to add x-class-extra-annotation and x-field-extra-annotation in your spec.
For instance for the Pet Clinic:
schemas:
Pet:
type: object
x-class-extra-annotation: "#javax.persistence.Entity"
required:
- id
- name
properties:
id:
type: integer
format: int64
x-field-extra-annotation: "#javax.persistence.Id"
name:
type: string
tag:
type: string
While the right way to solve this surely is an extension of swagger-codegen (probably with the introduction of some kind of include/exclude config), I got away with a fairly simply post-processing of the generated files.
In contrast to the OP I use Gradle instead of Maven and leveraged its extended filtering functionality. For Maven it is probably necessary to run a Groovy-script by way of the Groovy-Maven-Plugin, since Maven only supports placeholder substitution (as does Ant, so using the AntRun-Plugin would also not work).
I used a simple heuristic to only include entities with an id - the logic is as follows:
for all Java-files containing an ID-field
include import statement for javax.persistence.* after the package declaration
add the #Entity-annotation before the class definition
for the ID-field, add the annotations #Id and #GeneratedValue
(based on field names, other annotations - #OneToMany etc. - may be added as well)
Gradle-users may find the following task useful as a start:
task generateJpaAnnotations(type: Copy) {
from "${swaggerSources.<modelName>.code.outputDir}/src/main/java"
into "<output dir>
include '**/*.java'
eachFile {
if (it.file.text.contains("private Long id")) {
filter { line -> line.contains('package') ? "$line\nimport javax.persistence.*;" : line }
filter { line -> line.contains('public class') ? "#Entity\n$line" : line }
filter { line -> line.contains('private Long id') ? "#Id\n#GeneratedValue(strategy=GenerationType.AUTO)\n$line" : line } }
}
}
So I'm actually asking myself the same question.
I found an example but the guy is simply re-defining his POJOs and providing a way to adapt the generated ones to the handwritten ones. Tedious and not evolutive.
Globally this could be hard because I'm not sure there is a way in your swagger to decide which POJO will be JPA enabled and maybe you don't want them all in your DB (?) Also, how to you tag the id in swagger?
If you know of such a way, you can always modify the mustache (pojo.mustache I guess) to give you the annotations you're missing.

How to define an explicit binding with maven configuration : Coordinates

When i'm generating java classes from jooq generator, i get one field deprecated :
/**
* #deprecated Unknown data type. Please define an explicit {#link org.jooq.Binding} to specify how this type should be handled. Deprecation can be turned off using <deprecationOnUnknownTypes/> in your code generator configuration.
*/
#java.lang.Deprecated
public final TableField<PositionRecord, Object> COORDINATES = createField("coordinates", org.jooq.impl.DefaultDataType.getDefaultDataType("point"), this, "");
I am not sure to know how to define the binding on my maven configuration with the "Point" type.
Any ideas ?
Edit :
<configuration>
<!-- JDBC connection parameters -->
<jdbc>
<driver>com.mysql.jdbc.Driver</driver>
<url>jdbc:mysql://localhost:3306/${jooq.generation.schema}</url>
<user>${jooq.generation.user}</user>
<password>${jooq.generation.password}</password>
</jdbc>
<!-- Generator parameters -->
<generator>
<generate>
<javaTimeTypes>true</javaTimeTypes>
</generate>
<database>
<name>org.jooq.util.mysql.MySQLDatabase</name>
<includes>.*</includes>
<dateAsTimestamp>true</dateAsTimestamp>
<!-- In case your database supports catalogs, e.g. SQL Server:
<inputCatalog>public</inputCatalog>
-->
<inputSchema>${jooq.generation.schema}</inputSchema>
</database>
<target>
<packageName>${jooq.generation.package}</packageName>
<directory>target/generated-sources/jooq</directory>
</target>
</generator>
</configuration>
So far i've been able to use my java classes generated by this configuration. But some fields are deprecated because of some "Data type" not recognized. So i've found out that we could kinda use "force type" and put it in the maven configuration + adding the corresponding binding to let know jooq about this data type. For instance in our case : we want to be able to reach the field "Coordinates" ( as a Point type in java). I hope i was clear enough.
You've already answered your question:
So i've found out that we could kinda use "force type" and put it in the maven configuration + adding the corresponding binding to let know jooq about this data type
That's the correct way, you need to use a data type binding: https://www.jooq.org/doc/latest/manual/code-generation/custom-data-type-bindings
Follow the instructions from the manual and you'll be fine.

Persuade jOOQ not to prefix table names with the database name [duplicate]

I have 2 similar schemas in simple database - "develop" and "stage". I have generated Java classes with Jooq for one of that schemas ("develop" for example). When jooq generates query to db, it implicitly add schema's name to all query's aliases
select "develop"."image"."id", "develop"."image"."image_data"
from "develop"."image"
where "develop"."image"."id" = ?
So my question is, whether there are the way to change jooq schema name (for "stage" as an example) in generated query without regenerating jooq's classes for "stage" schema?
You have several options, which can even be combined:
Use the code generator's schema mapping feature
If you want to avoid hard-wiring the "develop" schema name into your generated classes, you can rewrite that to some other schema name like this:
<configuration>
<generator>
<database>
<schemata>
<schema>
<inputSchema>develop</inputSchema>
<outputSchema>stage</outputSchema>
</schema>
...
Of course, this just postpones the problem, because the schema name is still in the generated code. You can remove the name entirely from generated code by using the following option:
<configuration>
<generator>
<database>
<schemata>
<schema>
<inputSchema>develop</inputSchema>
<outputSchemaToDefault>true</outputSchemaToDefault>
</schema>
...
This will now remove any schema references from generated code, so the generated classes can run on all your schemas (be sure to use the correct connection and search_path, of course!)
I think this is the best option for you
More details here:
https://www.jooq.org/doc/latest/manual/code-generation/codegen-advanced/codegen-config-catalog-and-schema-mapping/
Use the runtime schema mapping feature
You can leave generated code as it is and rewrite all object references at runtime using Settings (which you supply to your jOOQ runtime Configuration). Again, you have the same two options:
Mapping the schema name:
new Settings().withRenderMapping(new RenderMapping()
.withSchemata(new MappedSchema()
.withInput("develop")
.withOutput("stage")
)
);
Removing all schema names:
new Settings().withRenderSchema(false);
More details here:
https://www.jooq.org/doc/latest/manual/sql-building/dsl-context/custom-settings/settings-render-mapping/

Spring #QuerydslPredicate Questions

Libaries Used
Spring Boot 1.3.2.RELEASE
QueryDSL 3.7.2
QueryDSL Maven Plugin 1.1.3
Hibernate 4.3.11.Final
Issue
Currently, I have a Spring Boot application that has some basic CRUD functionality using Spring Data JPA (backed by Hibernate), and auditing using Spring Data Envers. I also have the following endpoint to retrieve a list of entities from:
http://localhost:8080/test-app/list
Now, I wanted to use the new QueryDSL support that Spring offers through the #QuerydslPredicate annotation. This works fine for most fields or sub-entities, but it doesn't appear to work for collections of sub-entities. The documentation, blog posts, etc. don't seem to cover this case - and the only information I could find is that it supports "in" for simple collections (i.e. collections of String, etc.).
So, my entity is set up something like so:
Person.java
#Data
#Entity
#Audited
public class Person {
#Id
private long id;
private String name;
private List<Pet> pets = new ArrayList<>();
}
Pet.java
#Data
#Entity
#Audited
public class Pet {
#Id
private long id;
private int age;
}
I generate my Q classes using the com.mysema.maven:apt-maven-plugin, which generates my QPerson with the following field:
public final ListPath<com.test.Pet, com.test.QPet> pets = this.<com.test.Pet, com.test.QPet>createList("pets", com.test.Pet.class, com.test.QPet.class, PathInits.DIRECT2);
If I try to query on this though, I get an exception:
Query:
http://localhost:8080/test-app/list?pets.age=5
Exception:
10:21:37,523 ERROR [org.springframework.boot.context.web.ErrorPageFilter] (http-/127.0.0.1:8080-1) Forwarding to error page from request [/list] due to exception [null]: java.lang.NullPointerException
at org.springframework.util.ReflectionUtils.getField(ReflectionUtils.java:143) [spring-core-4.2.4.RELEASE.jar:4.2.4.RELEASE]
at org.springframework.data.querydsl.binding.QuerydslPredicateBuilder.reifyPath(QuerydslPredicateBuilder.java:185) [spring-data-commons-1.11.2.RELEASE.jar:]
at org.springframework.data.querydsl.binding.QuerydslPredicateBuilder.reifyPath(QuerydslPredicateBuilder.java:188) [spring-data-commons-1.11.2.RELEASE.jar:]
at org.springframework.data.querydsl.binding.QuerydslPredicateBuilder.getPath(QuerydslPredicateBuilder.java:167) [spring-data-commons-1.11.2.RELEASE.jar:]
at org.springframework.data.querydsl.binding.QuerydslPredicateBuilder.invokeBinding(QuerydslPredicateBuilder.java:136) [spring-data-commons-1.11.2.RELEASE.jar:]
at org.springframework.data.querydsl.binding.QuerydslPredicateBuilder.getPredicate(QuerydslPredicateBuilder.java:111) [spring-data-commons-1.11.2.RELEASE.jar:]
at org.springframework.data.web.querydsl.QuerydslPredicateArgumentResolver.resolveArgument(QuerydslPredicateArgumentResolver.java:106) [spring-data-commons-1.11.2.RELEASE.jar:]
at org.springframework.data.web.querydsl.QuerydslPredicateArgumentResolver.resolveArgument(QuerydslPredicateArgumentResolver.java:48) [spring-data-commons-1.11.2.RELEASE.jar:]
at org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:78) [spring-web-4.2.4.RELEASE.jar:4.2.4.RELEASE]
Now this query look like it's trying to resolve the propertyPath Person.pets.age. It correctly identifies Person.pets as a ListPath, and then tries to identify CompanyAddress.addressLine1 (which seems correct). The problem is, it tries to use the entity path to get the class, which is the ListPath instead of the QPet:
Field field = ReflectionUtils.findField(entityPath.getClass(), path.getSegment());
Object value = ReflectionUtils.getField(field, entityPath);
The following query works as expected:
http://localhost:8080/test-app/list?name=Bob
My expectation was that by using ?pets.age=5, the following predicate would be built:
QPerson.person.pets.any().age.eq(5)
Is this currently possible with Spring's QuerydslPredicate support? Or should I manually build the predicates from the query parameters?
Additional Question
As an additional question, is the following possible with QuerydslPredicate. Say I have a firstName and lastName on pet, and I want to run a query with just name=Bob:
http://localhost:8080/test-app/pet/list?name=Bob
I would want the query predicate to be built like this:
final BooleanBuilder petBuilder = new BooleanBuilder();
petBuilder.and(QPet.firstName.equals("Bob").or(QPet.lastName.equals("Bob")));
Is that possible? From looking at the customize method of the QuerydslBinderCustomizer it doesn't seem like it would be, since you need to bind off a field of the Q class. I'm guessing that what I want to do is not supported.
If these aren't possible, then I'll stick with manually creating the predicate, and passing that on to the repository.
You can use QuerydslBinderCustomizer to achieve your purpose. Heres some sample code that can help you out:
public interface PersonRepository extends JpaRepository<Job, Integer>,
QueryDslPredicateExecutor<Person>, QuerydslBinderCustomizer<QJob> {
#Override
public default void customize(final QuerydslBindings bindings, final QPerson person) {
bindings.bind(person.pets.any().name).first((path, value) -> {
return path.eq(value);
});
}
}
I ran into the same error. However I noticed that using the QuerydslAnnotationProcessor plugin (instead of the JPA annotation processor) allows me to query sub collections of entities as expected. You just have to mark all of your entity classes with the #QueryEntity annotation. (The JPA annotation processor automatically generates query classes for #Entity annotated classes.)
In your pom:
<plugin>
<groupId>com.mysema.maven</groupId>
<artifactId>apt-maven-plugin</artifactId>
<version>1.1.3</version>
<executions>
<execution>
<phase>generate-sources</phase>
<goals>
<goal>process</goal>
</goals>
<configuration>
<outputDirectory>target/generated-sources/annotations</outputDirectory>
<processor>com.querydsl.apt.QuerydslAnnotationProcessor</processor>
</configuration>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>com.querydsl</groupId>
<artifactId>querydsl-apt</artifactId>
<version>4.1.3</version>
</dependency>
</dependencies>
</plugin>
I'm believe I was running into the exception you encountered because I changed from the JPA Annotation Processor to the QuerydslAnnotationProcessor, for some reason I do not recall, and neglected to mark the entity class of the list in question with the #QueryEntity annotation. However I also believe I have another Spring-Data-Rest\JPA backed API that uses the JPA Annotation Processor built in August 2017, and I believe querying sub collections of entities works as expected. I'll be able to confirm that later today, and provide the versions of the relevant dependencies if that is the case. Perhaps this issue has been fixed.

Olingo - Create strongly typed POJOs for client library of OData service

I'm using Apache Olingo as an OData client for a Java SDK that I will provide for a RESTful OData API. In the SDK I want to be able to have strongly typed classes to represent the OData entities. I'm having trouble implementing this easily and thus feel like I'm missing a different strategy here.
The Olingo way seems to be to get an ODataClient object which provides the user with a bunch of useful methods for interacting with the API. The ODataClient is using a bunch of factory methods to build my request. For instance, this is the code I used to get the Customers from the Northwind sample OData service. client is an an instance of the necessary ODataClient class.
String serviceRoot = "http://services.odata.org/V4/Northwind/Northwind.svc";
URI customersUri = client.newURIBuilder(serviceRoot)
.appendEntitySetSegment("Customers").build();
ODataRetrieveResponse<ODataEntitySetIterator<ODataEntitySet, ODataEntity>> response =
client.getRetrieveRequestFactory().getEntitySetIteratorRequest(customersUri).execute();
if (response.getStatusCode() >= 400) {
log("Error");
return;
}
ODataEntitySetIterator<ODataEntitySet, ODataEntity> iterator = response.getBody();
while (iterator.hasNext()) {
ODataEntity customer = iterator.next();
log(customer.getId().toString());
}
I'd like to end up with a strongly typed entity from the iterator (i.e. Customer customer = iterator.next()). However, I'm not sure how to actually do that.
If I create a Customer class that extends ODataEntity and attempt to perform a cast such as Customer customer = (Customer) iterator.next() then I get a ClassCastException since the objects in the iterator are just ODataEntity objects and know nothing about the Customer subclass.
My next thought was to introduce generics but doing so will require what seems like a good amount of modification to the Olingo library which leads me to think that there is a better way to do this.
I'm using the development version of Apache Olingo 4 since the OData service must use OData 4.
What am I missing?
It is not really advertised, but there is nowadays a POJO generator in Olingo, in the source tree at ext / pojogen-maven-plugin. Unfortunately for using the POJOs another layer with a different programming model is added, which holds entities cached in memory and syncs with OData service on a flush operation. I would be really interested in adapting it to a more conventional request/response model based on Olingos Request Factories.
However, you could try it out. In your pom include pojogen-maven-plugin and odata-client-proxy.
The POJO generation can be triggered in the pom with
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<version>1.8</version>
<executions>
<execution>
<phase>process-sources</phase>
<goals>
<goal>add-source</goal>
</goals>
<configuration>
<sources>
<source>${project.build.directory}/generated-sources</source>
</sources>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.olingo</groupId>
<artifactId>pojogen-maven-plugin</artifactId>
<version>4.2.0-SNAPSHOT</version>
<configuration>
<outputDirectory>${project.build.directory}/generated-sources</outputDirectory>
<localEdm>${basedir}/src/main/resources/metadata.xml</localEdm>
<basePackage>odata.test.pojo</basePackage>
</configuration>
<executions>
<execution>
<id>v4pojoGen</id>
<phase>generate-sources</phase>
<goals>
<goal>v4pojoGen</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
For the experiment I stored the EDM Metadataof the Olingo Car example service at src/main/resources/metadata.xml. Somehow the plugin wants to create an inbetween ojc-plugin folder and I just moved the generated Java code at the proper place manually.
At that point you have a Service.java and Java interfaces for each entity or complex type in the EDM model.
You can make use of it to read some entities like this
Service<EdmEnabledODataClient> service = odata.test.pojo.Service.getV4("http://localhost:9080/odata-server-sample/cars.svc");
Container container = service.getEntityContainer(Container.class);
for (Manufacturer m : container.getManufacturers()) {
System.out.println(m.getName());
}

Categories

Resources