This short tutorial shows how to create a Quarkus application which exposes a set of JAX-RS endpoints which are backed by a repository of data managed with JPA:
Create a basic Quarkus project first:
mvn io.quarkus:quarkus-maven-plugin:1.4.2.Final:create \
-DprojectGroupId=org.acme \
-DprojectArtifactId=quarkus-hibernate \
-DclassName="org.acme.ExampleResource" \
-Dpath="/customer"
Depending on the Database you will use, you need to include the appropriate JDBC Driver. Here is the full list of dependencies you will need to run/test your application with hibernate-orm and PostgreSQL:
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-resteasy</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-hibernate-orm</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-resteasy-jsonb</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-jdbc-postgresql</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-junit5</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>rest-assured</artifactId>
<scope>test</scope>
</dependency>
Notice we have included also JSON-B dependency. That is required as our Endpoint consumes and produces JSON data. Here is the Endpoint Class which contains CRUD methods coupled with GET/POST/PUT/DELETE HTTP methods:
package org.acme;
import org.jboss.resteasy.annotations.jaxrs.PathParam;
import javax.inject.Inject;
import javax.persistence.EntityManager;
import javax.transaction.Transactional;
import javax.ws.rs.*;
import javax.ws.rs.core.Response;
@Path("/customer")
@Produces("application/json")
@Consumes("application/json")
public class ExampleResource {
@Inject
EntityManager entityManager;
@GET
public Customer[] get() {
return entityManager.createNamedQuery("Customers.findAll", Customer.class)
.getResultList().toArray(new Customer[0]);
}
@POST
@Transactional
public Response create(Customer customer) {
if (customer.getId() != null) {
throw new WebApplicationException("Id was invalidly set on request.", 422);
}
System.out.println("Creating "+customer);
entityManager.persist(customer);
return Response.ok(customer).status(201).build();
}
@PUT
@Transactional
public Customer update(Customer customer) {
if (customer.getId() == null) {
throw new WebApplicationException("Customer Id was not set on request.", 422);
}
Customer entity = entityManager.find(Customer.class, customer.getId());
if (entity == null) {
throw new WebApplicationException("Customer with id of " + customer.getId() + " does not exist.", 404);
}
entity.setName(customer.getName());
return entity;
}
@DELETE
@Transactional
public Response delete(Customer customer) {
Customer entity = entityManager.find(Customer.class, customer.getId());
if (entity == null) {
throw new WebApplicationException("Customer with id of " + customer.getId() + " does not exist.", 404);
}
entityManager.remove(entity);
return Response.status(204).build();
}
}
Now we will code the Entity class. This class includes an example of @NamedQuery to load the list of Customer objects and the definition of a Sequence which will generate the Customer id. As we will initially add some Customer records with the import.sql script, we will set as initialValue 10:
package org.acme;
import javax.persistence.*;
@Entity
@NamedQuery(name = "Customers.findAll", query = "SELECT c FROM Customer c ORDER BY c.name")
public class Customer {
private Long id;
private String name;
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "customerSequence")
@SequenceGenerator(name = "customerSequence", sequenceName = "customerSeq", allocationSize = 1, initialValue = 10)
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Customer{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
Finally, the configuration of the Database connection. The standard configuration file for Quarkus is placed in META-INF/application.properties :
quarkus.datasource.db-kind=postgresql
quarkus.datasource.username=quarkus
quarkus.datasource.password=quarkus
quarkus.datasource.jdbc.url=jdbc:postgresql://localhost/quarkusdb
quarkus.datasource.jdbc.max-size=8
quarkus.datasource.jdbc.min-size=2
quarkus.hibernate-orm.database.generation=drop-and-create
quarkus.hibernate-orm.log.sql=true
quarkus.hibernate-orm.sql-load-script=import.sql
Please notice that the Hibernate properties can also be specified through the META-INF/persistence.xml file. This is especially useful if you are migrating from a Java EE application:
<persistence xmlns="http://xmlns.jcp.org/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence
http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd"
version="2.1">
<persistence-unit name="CustomerPU" transaction-type="JTA">
<description>My customer entities</description>
<properties>
<!-- Connection specific -->
<property name="hibernate.dialect" value="org.hibernate.dialect.PostgreSQL95Dialect"/>
<property name="hibernate.show_sql" value="true"/>
<property name="hibernate.format_sql" value="true"/>
<!--
Optimistically create the tables;
will cause background errors being logged if they already exist,
but is practical to retain existing data across runs (or create as needed) -->
<property name="javax.persistence.schema-generation.database.action" value="drop-and-create"/>
<property name="javax.persistence.validation.mode" value="NONE"/>
</properties>
</persistence-unit>
</persistence>
We have included an import.sql file to bootstrap the application with 3 Customer objects:
INSERT INTO Customer(id, name) VALUES (1, 'Batman'); INSERT INTO Customer(id, name) VALUES (2, 'Superman'); INSERT INTO Customer(id, name) VALUES (3, 'Wonder woman');
Here is the full structure of our application:
src
├── main
│ ├── docker
│ │ ├── Dockerfile.jvm
│ │ └── Dockerfile.native
│ ├── java
│ │ └── org
│ │ └── acme
│ │ ├── Customer.java
│ │ └── ExampleResource.java
│ └── resources
│ ├── application.properties
│ ├── import.sql
│ └── META-INF
│ └── resources
│ └── index.html
Now, before starting the application, make sure PostgreSQL Database is available:
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
Now we can start the Quarkus application in development mode:
mvn clean install quarkus:dev
__ ____ __ _____ ___ __ ____ ______
--/ __ \/ / / / _ | / _ \/ //_/ / / / __/
-/ /_/ / /_/ / __ |/ , _/ ,< / /_/ /\ \
--\___\_\____/_/ |_/_/|_/_/|_|\____/___/
2020-05-17 13:27:06,932 INFO [io.agr.pool] (Quarkus Main Thread) Datasource '<default>': Initial size smaller than min. Connections will be created when necessary
Hibernate:
drop table if exists Customer cascade
Hibernate:
drop sequence if exists customerSeq
Hibernate: create sequence customerSeq start 10 increment 1
Hibernate:
create table Customer (
id int8 not null,
name varchar(255),
primary key (id)
)
Hibernate:
INSERT INTO Customer(id, name) VALUES (1, 'Batman')
Hibernate:
INSERT INTO Customer(id, name) VALUES (2, 'Superman')
Hibernate:
INSERT INTO Customer(id, name) VALUES (3, 'Wonder woman')
2020-05-17 13:27:07,341 INFO [io.quarkus] (Quarkus Main Thread) quarkus-hibernate /hello (powered by Quarkus 1.4.2.Final) started in 2.054s. Listening on: http://0.0.0.0:8080
2020-05-17 13:27:07,343 INFO [io.quarkus] (Quarkus Main Thread) Profile dev activated. Live Coding activated.
2020-05-17 13:27:07,343 INFO [io.quarkus] (Quarkus Main Thread) Installed features: [agroal, cdi, hibernate-orm, jdbc-postgresql, narayana-jta, resteasy, resteasy-jsonb]
Let's check at first the list of Customer objects:
curl -s http://localhost:8080/customer | jq
[
{
"id": 1,
"name": "Batman"
},
{
"id": 2,
"name": "Superman"
},
{
"id": 3,
"name": "Wonder woman"
}
]
Let's use the POST method to insert a new Customer:
curl -d '{"name":"Spiderman"}' -H "Content-Type: application/json" -X POST http://localhost:8080/customer
{"id":10,"name":"Spiderman"}
Let's use the PUT method to modify an existing Customer:
curl -d '{"id":10,"name":"Hulk"}' -H "Content-Type: application/json" -X PUT http://localhost:8080/customer
{"id":10,"name":"Hulk"}
Let's use the DELETE method to delete an existing Customer:
curl -d '{"id":10,"name":"Hulk"}' -H "Content-Type: application/json" -X DELETE http://localhost:8080/customer
{"id":10,"name":"Hulk"}
Let's check again the list of Customer objects:
curl -s http://localhost:8080/customer | jq
[
{
"id": 1,
"name": "Batman"
},
{
"id": 2,
"name": "Superman"
},
{
"id": 3,
"name": "Wonder woman"
}
]
Great! We have just learnt how to design a basic CRUD application using Quarkus, JPA and JAX-RS
Source code: https://github.com/fmarchioni/mastertheboss/tree/master/quarkus/hibernate