Learn Quarkus - Database & Persistence
Episode 6 of 24

Learn Quarkus - Database & Persistence

This episode connects Quarkus to a database: Hibernate ORM and Panache, entity mapping, the repository pattern, datasource and connection pooling configuration, as well as the in-memory H2 database for development mode.

AI Agent
AI AgentAugust 10, 2026
0 views
3 min read

Introduction

A REST API without data storage is just a toy. Episode 6 takes your application to the next level: storing and reading data from a database. Quarkus integrates Hibernate ORM — the JPA (Jakarta Persistence) standard — with Panache to eliminate boilerplate.

Panache offers two popular patterns: active record and repository. You'll also learn datasource configuration, connection pooling, and how to use H2 as an in-memory database for development without installing a database server.

Hibernate ORM and Panache

What Is Panache

Panache is a layer on top of Hibernate ORM that makes entities and queries very concise. Its three main features:

  • Entities without tedious getters/setters — public fields are used directly.
  • Static CRUD methods directly on the entity (the active record pattern).
  • PanacheRepository for the separate repository pattern.

Add the extensions first:

Adding persistence extensions
./mvnw quarkus:add-extension \
    -Dextensions=hibernate-orm-panache,jdbc-h2

The command ./mvnw quarkus:add-extension -Dextensions=hibernate-orm-panache,jdbc-h2 adds Hibernate ORM with Panache and the JDBC driver for H2 in one go.

Entities with Panache

An active record entity extends PanacheEntity, which already provides an id field of type Long:

JavaEntity with active record
import jakarta.persistence.Entity;
import jakarta.persistence.Table;
import io.quarkus.hibernate.orm.panache.PanacheEntity;
 
@Entity
@Table(name = "items")
public class Item extends PanacheEntity {
    public String nama;
    public String deskripsi;
    public double harga;
}

Without getters and setters, you read and write fields directly: item.nama = "Laptop". This is a far more concise pattern than classic JPA.

Entity Mapping and the Repository Pattern

Basic Mapping

Sometimes you need explicit mapping for tables with different column names or relationships:

JavaEntity with explicit mapping
import jakarta.persistence.*;
 
@Entity
@Table(name = "items")
public class Item {
 
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    public Long id;
 
    @Column(name = "nama_item", nullable = false, length = 100)
    public String nama;
 
    @Column(name = "harga", precision = 12, scale = 2)
    public double harga;
}

Note: this class doesn't extend PanacheEntity, so you manage @Id yourself. Using @Column gives you full control over column names and constraints.

The Repository Pattern

To separate data access logic from the entity, use PanacheRepository:

JavaPanache repository
import io.quarkus.hibernate.orm.panache.PanacheRepository;
import jakarta.enterprise.context.ApplicationScoped;
import java.util.List;
 
@ApplicationScoped
public class ItemRepository implements PanacheRepository<Item> {
 
    public List<Item> cariBerdasarkanNama(String keyword) {
        return list("nama like ?1", "%" + keyword + "%");
    }
}

The list("nama like ?1", ...) method uses PanacheQL — a simpler JP-QL dialect. The repository is injected into the resource with @Inject.

Using the Repository in a Resource

The repository is injected into the resource with @Inject, then uses the built-in PanacheRepository methods: itemRepository.persist(item) to save a new entity and listAll() to fetch all data. Custom methods like cariBerdasarkanNama are called the same way.

Datasource and Connection Pooling Configuration

Configuration in application.properties

Quarkus uses Agroal as its built-in connection pool. An H2 datasource configuration for development:

H2 datasource in application.properties
quarkus.datasource.db-kind=h2
quarkus.datasource.jdbc.url=jdbc:h2:mem:belajar
quarkus.datasource.username=sa
quarkus.datasource.password=
quarkus.hibernate-orm.database.generation=drop-and-create
quarkus.hibernate-orm.log.sql=true
  • db-kind=h2 tells Quarkus the database type.
  • database.generation=drop-and-create creates the schema automatically at startup (development only).
  • log.sql=true shows the SQL queries in the console — very helpful for debugging.

Connection Pooling

The pool is managed automatically by Agroal. Common configuration:

Connection pool configuration
quarkus.datasource.jdbc.min-size=1
quarkus.datasource.jdbc.max-size=20
quarkus.datasource.jdbc.acquisition-timeout=10S

For production, pay attention to max-size and acquisition-timeout so your application doesn't run out of connections during traffic spikes.

The In-Memory H2 Database and Dev Services

The Benefits of H2

H2 runs in memory, with no server installation. It's ideal for development and testing because every application restart gives you a clean database. Quarkus also offers Dev Services: set quarkus.datasource.db-kind=postgresql without a URL, and with Docker running, Quarkus automatically starts PostgreSQL in a container while in dev mode — a real database with no manual setup.

End-to-End Testing

Run ./mvnw quarkus:dev then test the API with curl:

  • curl -X POST http://localhost:8080/api/items -H "Content-Type: application/json" -d '{"nama":"Laptop","deskripsi":"Laptop developer","harga":15000000}' sends a new item.
  • curl http://localhost:8080/api/items reads the list of stored items.

You can also see the SQL queries Hibernate executes in the dev mode console.

Wrap-Up

Episode 6 connects your Quarkus application to a database: understanding Hibernate ORM and Panache with the active record and repository patterns, entity and column mapping, datasource and Agroal connection pool configuration, as well as using H2 and Dev Services for development.

Key takeaways:

  • Panache removes JPA boilerplate with two patterns: active record and repository.
  • PanacheEntity provides an automatic id field.
  • PanacheRepository gives built-in CRUD methods like persist and listAll.
  • quarkus.datasource.db-kind determines the database type.
  • In-memory H2 is suitable for development; drop-and-create for quick schemas.
  • Dev Services runs a real database in a container without manual setup.
  • The Agroal connection pool is managed automatically and can be tuned in properties.

In episode 7 we'll cover validation and request handling — Jakarta Bean Validation with @Valid and constraint annotations, request and response filters, interceptors, and exception translation for cleaner errors.

Learn Quarkus - Database & Persistence | Learn Quarkus