In this tutorial we will learn how to generate automatically a JAX-RS CRUD application in Quarkus, starting from a Hibernate Panache Entity.
Creating CRUD applications for simple REST endpoint is a tedious task which requires adding lots of boilerplate code. Thanks to the quarkus-hibernate-orm-panache extension, this can be self-generated starting from a plain Entity class using or not a Respository class for persistence.
Please note that this feature is still experimental and currently supports Hibernate ORM with Panache and can generate CRUD resources that work with application/json and application/hal+json content.
First, check this tutorial for some background on Hibernate Panache: Managing Data Persistence with Quarkus and Hibernate Panache
Setting up the Quarkus project
We will now re-create the same application using self-generation of Resource Endpoints. Start by creating a new Quarkus project:
mvn io.quarkus:quarkus-maven-plugin:1.6.1.Final:create \ -DprojectGroupId=com.mastertheboss \ -DprojectArtifactId=panache-demo \ -DclassName="com.mastertheboss.MyService" \ -Dpath="/tickets" \ -Dextensions="quarkus-hibernate-orm-rest-data-panache,quarkus-hibernate-orm-panache,quarkus-jdbc-postgresql,quarkus-resteasy-jsonb"
As you can see, our pom.xml file includes an extra dependency to support JAX-RS self-generation with Panache:
<dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-hibernate-orm-rest-data-panache</artifactId> </dependency>
We will be adding a simple Entity objects, named Tickets which is unchanged from our first Panache example:
import javax.persistence.Column; import javax.persistence.Entity; import io.quarkus.hibernate.orm.panache.PanacheEntity; @Entity public class Ticket extends PanacheEntity { @Column(length = 20, unique = true) public String name; @Column(length = 3, unique = true) public String seat; public Ticket() { } public Ticket(String name, String seat) { this.name = name; this.seat = seat; } }
Then, you have two options here. If you are updating the Entity directly from the JAX-RS Resource, then just add an interface which extends PanacheEntityResource which is typed with the Entity and the Primary Key used behind the hoods by Panache (in our example, we default to Long)
import io.quarkus.hibernate.orm.rest.data.panache.PanacheEntityResource; public interface MyService extends PanacheEntityResource<Ticket, Long> { }
On the other hand, if your application uses a Repository Class, then you will use a different set of Types which includes also the TicketRepository class:
public interface MyService extends PanacheRepositoryResource<TicketRepository, Ticket, Long> { }
That’s all! You just add one typed interface and you are done with the JAX-RS Resource! As a proof of concept, let’s add openapi extension to our project so that we can eventually check all endpoints which have been created:
<dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-smallrye-openapi</artifactId> </dependency>
Since our application will run against PostgreSQL, in our application.properties includes the JDBC settings for connecting to a local instance of it:
quarkus.datasource.url=jdbc:postgresql:quarkusdb quarkus.datasource.driver=org.postgresql.Driver quarkus.datasource.username=quarkus quarkus.datasource.password=quarkus quarkus.hibernate-orm.database.generation=drop-and-create quarkus.hibernate-orm.log.sql=true
Now start PostgreSQL, for example using Docker:
docker run --ulimit memlock=-1:-1 -it --rm=true --memory-swappiness=0 --name quarkus_test -e POSTGRES_USER=quarkus -e POSTGRES_PASSWORD=quarkus -e POSTGRES_DB=quarkusdb -p 5432:5432 postgres:10.5
We also have included an import.sql file to create some sample records:
INSERT INTO ticket(id, name,seat) VALUES (nextval('hibernate_sequence'), 'Phantom of the Opera','11A'); INSERT INTO ticket(id, name,seat) VALUES (nextval('hibernate_sequence'), 'Chorus Line','5B'); INSERT INTO ticket(id, name,seat) VALUES (nextval('hibernate_sequence'), 'Mamma mia','21A');
And the following class will run a minimal Test Case:
import io.quarkus.test.junit.QuarkusTest; import org.junit.jupiter.api.Test; import static io.restassured.RestAssured.given; import static org.hamcrest.CoreMatchers.containsString; @QuarkusTest public class MyServiceTest { @Test public void testListAllTickets() { given() .when().get("/my-service") .then() .statusCode(200) .body( containsString("Phantom of the Opera"), containsString("Chorus Line"), containsString("Mamma mia") ); } }
Testing the application
Now if you run the application using:
mvn install quarkus:dev
You will see the following endpoints are available at: http://localhost:8080/openapi
openapi: 3.0.1 info: title: Generated API version: "1.0" paths: /my-service: get: responses: "200": description: OK post: requestBody: content: application/json: schema: $ref: '#/components/schemas/Ticket' responses: "200": description: OK /my-service/{id}: get: parameters: - name: id in: path required: true schema: format: int64 type: integer responses: "200": description: OK content: application/json: schema: $ref: '#/components/schemas/Ticket' put: parameters: - name: id in: path required: true schema: format: int64 type: integer requestBody: content: application/json: schema: $ref: '#/components/schemas/Ticket' responses: "200": description: OK delete: parameters: - name: id in: path required: true schema: format: int64 type: integer responses: "204": description: No Content components: schemas: Ticket: type: object properties: id: format: int64 type: integer name: type: string seat: type: string
As you can see, our JAX-RS Service has been bound with the hypened name of the Backing resource: “my-service”. As a proof of concept, you can for example query the list of Ticket objects:
curl http://localhost:8080/my-service
That will return:
[ { "id":2, "name":"Chorus Line", "seat":"5B" }, { "id":3, "name":"Mamma mia", "seat":"21A" }, { "id":1, "name":"Phantom of the Opera", "seat":"11A" } ]
That’s all. We have demonstrated how to scaffold a CRUD JAX-RS application from an Entity object using Quarkus and Hibernate ORM Panache. Stay tuned for more updates on this matter: https://quarkus.io/guides/rest-data-panache
Source code for this tutorial: https://github.com/fmarchioni/mastertheboss/tree/master/quarkus/panache-rest-demo