How to create a Quarkus app with Hibernate — A step-by-step approach

Quarkus is a Kubernetes native full stack java framework that is optimized and built specifically for containers to support microservice and serverless applications . Quarkus comes from the Redhat family and offers a tight integration to Redhat Openshift .

Quarkus’ fast startup time and low memory footprint makes it an ideal choice for building container first applications .Quarkus is built on the Context and Dependency Injection (CDI) method and provides a extensions which can be plugged into add additional features .

In this article we are going to look at how quickly we can build and end to end app with a database connectivity with hibernate support .

There are two out of the box approaches that we are going to use here. 1) using the hibernate in the classical way 2 ) using hibernate with panache that provides additional and easy interfaces for ORM .

Step 1 : Yes we need a database !

Let’s quickly setup a database for our purpose and here we are going to use a PostgreSQL database that we will run as a container . Please check the below link ( to an earlier post of mine ) which will help you to spin up a PostgreSQL database with some data .

Step 2 : Let’s quickly create a Quarkus project

Quarkus provides a maven archetype which we can use to quickly scaffold a project structure and then customize it according to our specific needs .

Below given is a command with which we will create our project the airport-service

mvn io.quarkus:quarkus-maven-plugin:1.8.3.Final:create -DprojectGroupId=com.hkg.quarkus.airport -DprojectArtifactId=airport-service  - DclassName=”com.hkg.quarkus.airport.controller.AirportController” -Dpath=”/airport”

This creates a basic project structure with a JAX-RS resource class AirportController as given below .

package com.hkg.quarkus.airport.controller;import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
@Path(“/airport”)
public class AirportController {
@GET
@Produces(MediaType.TEXT_PLAIN)
public String hello() {
return “hello”;
}
}

The default method will return a string hello if you bootup the application and try the URL http://localhost:8080/airport at this point .

Use the maven command given below to start the app and test this

mvnw compile quarkus:dev

Step 3 : Adding the data access layer

Quarkus offers 2 out of the box solutions for building the data access layer as mentioned earlier .

Method 1 : Using the Entity Manager .

Let’s start by adding the required maven dependencies for this ; we are using a postgresql database so we will use the relevant JDBC drivers .

<! — Hibernate ORM specific dependencies →
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-hibernate-orm</artifactId>
</dependency>
<! — JDBC driver dependencies →
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-jdbc-postgresql</artifactId>
</dependency>

The next step is to create an Entity class for Airport .

Airport.java

package com.hkg.quarkus.airport.entity;import java.io.Serializable;import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.SequenceGenerator;
import javax.persistence.Table;
@Entity
@Table(name = “AIRPORT”)
public class Airport implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
@Id
@Column(name=”id”)
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = “apt_id_seq”)
@SequenceGenerator(name = “apt_id_seq”, sequenceName = “apt_id_seq”, allocationSize=1)
private Long id ;
@Column(name=”apt_code”)
private String airportCode;
@Column(name=”apt_name”)
private String airportName;
@Column(name=”city_name”)
private String cityName;
@Column(name=”country”)
private String countryName;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getAirportCode() {
return airportCode;
}
public void setAirportCode(String airportCode) {
this.airportCode = airportCode;
}
public String getAirportName() {
return airportName;
}
public void setAirportName(String airportName) {
this.airportName = airportName;
}
public String getCityName() {
return cityName;
}
public void setCityName(String cityName) {
this.cityName = cityName;
}
public String getCountryName() {
return countryName;
}
public void setCountryName(String countryName) {
this.countryName = countryName;
}
}

Now let’s create a repository class and inject the Entity manager into it and use the querying methods for creating the CRUD methods into it .

AirportRepository.java

package com.hkg.quarkus.airport.repository;import java.util.List;import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
import javax.persistence.EntityManager;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaDelete;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Root;
import javax.transaction.Transactional;
import com.hkg.quarkus.airport.entity.Airport;@ApplicationScoped
public class AirportRepository {
/**
*
*/
@Inject
EntityManager entityManager;
/**
*
* @param airport
* @return
*/
@Transactional
public Airport createOrUpdate(Airport airport) {
if (airport.getId() == null) {
this.entityManager.persist(airport);
return airport;
} else {
return this.entityManager.merge(airport);
}
}
/**
*
* @param id
*/
@Transactional
public void deleteById(Long id) {
CriteriaBuilder cb = this.entityManager.getCriteriaBuilder();
CriteriaDelete<Airport> delete = cb.createCriteriaDelete(Airport.class);
Root<Airport> root = delete.from(Airport.class);
delete.where(cb.equal(root.get(“id”), id));
this.entityManager.createQuery(delete).executeUpdate();
}
/**
*
* @return
*/
public List<Airport> getAllAirports() {
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<Airport> cq = cb.createQuery(Airport.class);
Root<Airport> rootEntry = cq.from(Airport.class);
CriteriaQuery<Airport> all = cq.select(rootEntry);
TypedQuery<Airport> allQuery = entityManager.createQuery(all);
return allQuery.getResultList();
}}

Quarkus allows you to configure the persistence units and datasource in the application.properties files instead of configuring a persistence-unit.xml . Alternatively you can configure it in a persistence-unit.xml as well .

application.properties

quarkus.datasource.db-kind = postgresql
quarkus.datasource.username = docker
quarkus.datasource.password = docker
quarkus.datasource.jdbc.url = jdbc:postgresql://localhost:5432/airport-db
quarkus.hibernate-orm.database.generation=update

Method 2 : Using Panache library with Repository Pattern .

In this method we will be using the same Entity class that we created in the Method 1 .

However the Repository class will be implementing an abstract Repository available in Panache library named PanacheReposity

The first step here is to add the relevant maven dependencies .

<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-hibernate-orm-panache</artifactId>
</dependency>
<! — JDBC driver dependencies →<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-jdbc-postgresql</artifactId>
</dependency>

AirportRepository.java

package com.hkg.quarkus.airport.repository;import java.util.List;import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
import javax.persistence.EntityManager;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaDelete;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Root;
import javax.transaction.Transactional;
import com.hkg.quarkus.airport.entity.Airport;import io.quarkus.hibernate.orm.panache.PanacheRepository;@ApplicationScoped
public class AirportRepository implements PanacheRepository<Airport> {
/***
*
* @param airport
* @return
*/
public Airport createOrUpdate(Airport airport) {
this.persist(airport);
return airport;
}

/**
*
* @return
*/
public List<Airport> getAllAirports() {
return this.listAll();
}

/**
*
* @param id
*/
public void delete(Long id) {
this.deleteById(id);
}
}

application.properties will be same as that of Method 1 .

Step 4 : Creating the Service and Injection of Repository

The next step is add a service class namely AirportService to access the Repository methods by injecting the repository into it.

package com.hkg.quarkus.airport.service;import java.util.List;import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
import javax.transaction.Transactional;
import com.hkg.quarkus.airport.entity.Airport;
import com.hkg.quarkus.airport.repository.AirportRepository;
@ApplicationScoped
public class AirportService {

@Inject
AirportRepository airportRepository;


@Transactional
public Airport save(Airport airport) {
return airportRepository.createOrUpdate(airport);
}

public List<Airport> getAllAirports() {
return airportRepository.getAllAirports();
}

@Transactional
public void deleteByid(Long id) {
airportRepository.deleteById(id);
}
}

Step 5 : Coding the JAX-RS layer

The final step in the process is to code the JAX-RS layer and add the require dependencies for the JSON conversion . We will be using the traditional JAX-RS interfaces and annotations for this .

First add the dependencies required . When you scaffold your project the pom.xml will have default dependency of resteasy implementation by quarkus as given below

<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-resteasy</artifactId>
</dependency>

In addition to this add the dependency for resteasy-jsonb , for json conversion of java objects at the JAX-RS layer

<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-resteasy-jsonb</artifactId>
</dependency>

AirportController.java

package com.hkg.quarkus.airport.controller;import javax.inject.Inject;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import static javax.ws.rs.core.Response.ok;
import java.util.List;import org.jboss.resteasy.annotations.jaxrs.PathParam;import com.hkg.quarkus.airport.entity.Airport;
import com.hkg.quarkus.airport.service.AirportService;
@Path(“/airport”)
public class AirportController {
@Inject
AirportService airporService;
@GET
@Path(“/getAll”)
@Produces(MediaType.APPLICATION_JSON)
public List<Airport> getAllAirports() {
return this.airporService.getAllAirports();
}
@POST
@Path(“/save”)
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Airport save(Airport airport) {
return this.airporService.save(airport);
}
@DELETE
@Path(“/delete/{id}/”)
public Response deleteOrderById(@PathParam(“id”) Long id) {
try {
this.airporService.deleteByid(id);
} catch (Exception e) {
return Response.status(Response.Status.OK).entity(“Delete failed”).build();
}
return Response.status(Response.Status.OK).entity(“Deleted successfully”).build();
}
}

We are now ready with the code , now let’s build and start the application . As I already mentioned use the following maven command for the same

mvnw compile quarkus:dev

When the application is up you can see a log as shown below .

Listening for transport dt_socket at address: 5005
__ ____ __ _____ ___ __ ____ ______
— / __ \/ / / / _ | / _ \/ //_/ / / / __/
-/ /_/ / /_/ / __ |/ , _/ ,< / /_/ /\ \
— \___\_\____/_/ |_/_/|_/_/|_|\____/___/
2020–10–19 21:38:39,258 WARN [io.qua.dep.QuarkusAugmentor] (main) Using Java versions older than 11 to build Quarkus applications is deprecated and will be disallowed in a future release!
2020–10–19 21:38:43,205 INFO [io.quarkus] (Quarkus Main Thread) airport-service 1.0-SNAPSHOT on JVM (powered by Quarkus 1.9.0.CR1) started in 4.153s. Listening on: http://0.0.0.0:8080
2020–10–19 21:38:43,206 INFO [io.quarkus] (Quarkus Main Thread) Profile dev activated. Live Coding activated.
2020–10–19 21:38:43,208 INFO [io.quarkus] (Quarkus Main Thread) Installed features: [agroal, cdi, hibernate-orm, hibernate-orm-panache, jdbc-postgresql, mutiny, narayana-jta, resteasy, resteasy-jsonb, smallrye-context-propagation]

Your application will be running on http://localhost:8080 now .

There will be 2 Dockerfiles created automatically generated with the project scaffolding if you want to use docker images of the application . One that builds an image based on JVM and the second one that builds a GraalVM native image .

As you would see Quarkus bootup time quite good and the ease of programming with the availability of variety of built in extensions makes it an interesting choice for building microservices and serverless applications easily .

Full sourcecode for the example that I used in this article is in the below given github repositories .

Techie , Travelista ,Philomath

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store