Quarkus & Hibernate – Getting Started


Take your skills to the next level!

The Persistence Hub is the place to be for every Java developer. It gives you access to all my premium video courses, monthly Java Persistence News, monthly coding problems, and regular expert sessions.


The Quarkus project enables you to develop Kubernetes-native applications based on Java and a huge list of well-established, Java-based frameworks. Hibernate is, of course, one of these frameworks.

But the project not only enables you to build native applications with incredibly fast boot times and a very small memory footprint. It also provides a bunch of usability features that make our job as developers easier.

This first article of my series on persistence layers for Quarkus-based applications will focus on using plain Hibernate. There is no huge difference compared to using Hibernate in a standard Java SE or Jakarta EE application. But there are a few things you need to know, like why you need to use compatible versions of your preferred frameworks and how to use the centralized configuration.

In future articles of this series, I will show you Panache. It’s a Quarkus-specific framework that sits on top of Hibernate. It makes implementing your persistence layer a lot easier by applying the active record or the repository pattern.

But let’s start by using plain Hibernate in a Quarkus application.

Creating a Quarkus Application with Hibernate

Before you can start implementing your domain model, you need to create an application and add the required dependencies. The easiest way to do that is to use the interactive project generator at https://code.quarkus.io/. It enables you to specify the metadata of your project and pick the required dependencies. If you want to use Hibernate, make sure to select “Hibernate ORM” and the JDBC driver of your preferred database. Based on this information, it then generates a project and provides it as a downloadable archive.

If you prefer setting up the project yourself, please follow the guides at quarkus.io and add a Quarkus-specific dependency to Hibernate and your JDBC driver to your project. Because Quarkus is based on GraalVM and supports the creation of native images, it imposes some technical constraints on the libraries and frameworks you want to use. Quite often, this makes it impossible to use the standard version of these frameworks. But don’t worry, a long list of the most popular Java frameworks already offer compatible versions, and you only need to reference the right artifact.

<!-- Hibernate ORM  -->
<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>

Configuring Hibernate

After you created your project and added the required dependencies, it’s time to configure Hibernate.This takes significantly less effort than for Java SE or Jakarta EE applications. You don’t need to create a persistence.xml file and copy several lines of standard configuration from an existing project. Quarkus generates that for you based on the dependencies you added to your project and a set of smart defaults.

But there are still a few things you need to configure, like the connection to your database, if Hibernate shall generate the database schema, and the logging of your application. You can configure all of this in the application.properties file.

The following example uses postgres as the username and password when connecting to the database test on localhost:5432. It also tells Hibernate to drop and create the database based on entity mappings and import the data.sql script.

# datasource configuration
quarkus.datasource.username = postgres
quarkus.datasource.password = postgres
quarkus.datasource.jdbc.url = jdbc:postgresql://localhost:5432/test

# drop and create the database at startup
quarkus.hibernate-orm.database.generation=drop-and-create
quarkus.hibernate-orm.sql-load-script=data.sql

Configuration Properties Supported by Quarkus

Here is a list of the most important configuration properties. You can find a complete list in the Quarkus documentation.

  • quarkus.datasource.username / quarkus.datasource.password
    The username and password that Quarkus shall use to connect to your database.
  • quarkus.datasource.jdbc.url
    The JDBC URL that Quarkus shall use to connect to your database. Hibernate will automatically pick a matching dialect but requires a matching JDBC driver at runtime.
  • quarkus.hibernate-orm.database.default-schema
    The default database schema to which Hibernate shall map all database objects.
  • quarkus.hibernate-orm.physical-naming-strategy / quarkus.hibernate-orm.implicit-naming-strategy
    The naming strategies  that Quarkus shall use to map your entity classes and properties to database tables and columns.
  • quarkus.hibernate-orm.second-level-caching-enabled
    Activate or deactivate Hibernate’s 2nd Level Cache. It’s activated by default, and you only need to activate caching in your entity mapping.
  • quarkus.hibernate-orm.statistics / quarkus.hibernate-orm.metrics.enabled
    If Hibernate’s statistics component is activated and if they are published using a metrics extension (default: false).
  • quarkus.hibernate-orm.log.sql / quarkus.hibernate-orm.log.format-sql / quarkus.hibernate-orm.log.bind-parameters
    If Hibernate shall log SQL statements, format them, and include bind parameter values.

Defining your Entities

You define your entities in the same way as for any other Hibernate-based application. For a basic default mapping, you implement a Java class with the same name as your database table, add a default constructor and annotate it with @Entity. And for each database column, you want to map, you add a private attribute with the same name. To comply with the JPA specification, you should also provide a getter and setter method for each attribute.

Here you can see an example of a ChessPlayer entity that Hibernate maps to the chessplayer table.

@Entity
public class ChessPlayer {

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "player_seq")
    @SequenceGenerator(name = "player_seq", sequenceName = "player_seq")
    private Long id;

    private String firstName;

    private String lastName;

    private LocalDate birthDate;

    @Version
    private int version;

    ...
}

The id attribute maps the primary key column, and Hibernate uses the database sequence player_seq to generate unique primary key values. The attributes firstName, lastName, and birthDate get mapped to columns with the same name. And Hibernate uses the version attribute to prevent concurrent modifications by applying the optimistic locking algorithm.

I expect you to be familiar with JPA’s and Hibernate’s basic mappings and query features for this article. If that’s not the case, I recommend reading some of my beginner articles or joining my JPA for Beginners online course.

Implementing your Business Code

In this article, we’re using plain Hibernate. So, it shouldn’t be a surprise if I tell you that you can use it in the same way as in any other Jakarta EE application. This will change when we use Panache in future articles of this series.

The easiest way to get an EntityManager instance is to inject it using the @Inject annotation. This is the same approach as you’re probably already using in your Jakarta EE application.

@Inject
EntityManager entityManager;

You can then use this EntityManager to read entities or DTOs using JPQL, native, or Criteria queries in your business code

List<ChessPlayer> chessPlayers = entityManager.createQuery("Select cp from ChessPlayer cp", ChessPlayer.class).getResultList();

As usual, all entity objects you load from the database or for which you call the persist method are in the lifecycle state managed. Hibernate will include them in its dirty checks and automatically flush all changes to the database.

ChessPlayer chessPlayer = new ChessPlayer();
chessPlayer.setFirstName("Thorben");
chessPlayer.setLastName("Janssen");

entityManager.persist(chessPlayer);

Conclusion

You can use Hibernate with Quarkus in almost the same way as in a Java SE or Jakarta EE application. The only differences are the required dependencies and the configuration of your application.

Quarkus is based on GraalVM, which introduces some technical limitations on the code that will be executed. This provides almost no limitations to the code you write yourself, but it made many Java frameworks unusable. Fortunately, a long list of the most popular Java frameworks has been adjusted to work in this environment. Please make sure to always reference these versions in your dependencies. This is automatically the case if you create your project using https://code.quarkus.io/.

By default, Quarkus provides most of Hibernate’s configuration based on smart defaults and the dependencies available on the classpath. You can adjust all defaults and add your own configuration parameters to the application.properties file.