Overview
Panache is a Quarkus framework-specific library that simplifies the development of Hibernate-based persistence layers. It reduces boilerplate code by providing built-in CRUD operations and type-safe queries.
Panache supports both the Active Record pattern, which embeds database operations directly in entity classes, and the Repository pattern, which separates data access logic into dedicated repository classes. It integrates with Quarkus to provide fast startup times and a low memory footprint. Panache works with Hibernate Object-Relational Mapping (ORM) and Java Persistence API (JPA) to simplify data access while keeping code clean.
In this tutorial, you build a Quarkus application with Panache to perform CRUD operations and aggregations on a MongoDB database. The application allows you to add, update, find, and delete books, as well as count books by genre.
Tutorial
In this tutorial, you perform the following actions:
Verify the prerequisites
Set up the project
Create the data model
Create REST API endpoints
Test the API
Verify the prerequisites
Before you begin, ensure you have the following:
A MongoDB Atlas account with a configured cluster. To learn how to set up an Atlas cluster, see the MongoDB Get Started guide.
Java version 21 or later. To learn more about installing Java, see the Oracle website.
Your preferred IDE.
Maven to manage project dependencies.
Set up the project
Create a folder to hold your Quarkus project. Navigate into this directory and create a Quarkus project with Maven by running the following command:
mvn io.quarkus:quarkus-maven-plugin:create \ -DprojectGroupId=io.quarkus.platform \ -DprojectArtifactId=quarkus-bom \ -DclassName="com.example.BookResource" \ -Dpath="/books"
Add the following dependencies to your pom.xml file:
<dependencies> <dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-arc</artifactId> </dependency> <dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-rest</artifactId> </dependency> <dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-rest-client-jackson</artifactId> <version>3.16.2</version> </dependency> <dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-resteasy-reactive-jackson</artifactId> <version>3.15.1</version> </dependency> <dependency> <groupId>io.rest-assured</groupId> <artifactId>rest-assured</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-mongodb-panache</artifactId> </dependency> </dependencies>
Build the application and download the dependencies by running the following command in your project directory:
mvn clean install
Create the data model
First, from the root directory of your project, navigate to src/main/java/com/<package>.
Create a Book.java file in this directory . Inside this file, define the Book
class as shown in the following code:
import io.quarkus.mongodb.panache.PanacheMongoEntity; import io.quarkus.mongodb.panache.common.MongoEntity; public class Book extends PanacheMongoEntity { public String title; public String author; public String genre; public int year; public Book() { } }
This class represents a book document in the MongoDB collection with fields for the title, author, genre, and year.
Create REST API endpoints
Create a BookRepository.java file in your package directory. Inside this file,
define the BookRepository class as shown in the following code:
import com.quarkusWithPanache.model.Book; import io.quarkus.mongodb.panache.PanacheMongoRepository; import jakarta.enterprise.context.ApplicationScoped; public class BookRepository implements PanacheMongoRepository<Book> { }
Next, create a BookResource.java file in your package directory.
Inside this file, define a resource class that uses the BookRepository
interface you created in the preceding example, as shown in the
following code:
import com.mongodb.client.MongoClient; import com.mongodb.client.MongoCollection; import com.quarkusWithPanache.model.Book; import com.quarkusWithPanache.repository.BookRepository; import jakarta.inject.Inject; import jakarta.ws.rs.*; import jakarta.ws.rs.core.Response; import org.bson.Document; import org.bson.types.ObjectId; import java.util.ArrayList; import java.util.List; public class BookResource { BookRepository bookRepository; MongoClient mongoClient; private MongoCollection<Document> getCollection() { return mongoClient.getDatabase("test").getCollection("books"); } public BookResource(BookRepository bookRepository) { this.bookRepository = bookRepository; } // Define API endpoints here }
The following sections show how to implement various API endpoints for CRUD operations and aggregations. You can add the code for each endpoint below the highlighted line in the preceding example.
Insert one book
You can insert a single book by adding the following code to your resource class:
BookRepository bookRepository; public Response addBook(Book book) { bookRepository.persist(book); return Response.status(Response.Status.CREATED).entity(book).build(); }
Bulk insert
You can insert multiple books at once by adding the following code to your resource class:
public Response bulkAddBooks(List<Book> books) { // Prepare documents for bulk write List<Document> documents = new ArrayList<>(); for (Book book : books) { documents.add(new Document("title", book.title) .append("author", book.author) .append("genre", book.genre) .append("year", book.year)); } getCollection().insertMany(documents); return Response.status(Response.Status.CREATED).entity(books).build(); }
The getCollection method returns the database and
collection name from the com.mongodb.client.MongoCollection
package.
Find all books
You can retrieve all books by adding the following code to your resource class:
public List<Book> getAllBooks() { return bookRepository.listAll(); }
Find one book by ID
You can retrieve a specific book by its _id value by
adding the following code to your resource class:
public Book getBookById( String id) { return bookRepository.findById(new ObjectId(id)); }
Delete a book by ID
You can delete a book by its _id value by adding the
following code to your resource class:
public Response deleteBook( String id) { boolean deleted = bookRepository.deleteById(new ObjectId(id)); return deleted ? Response.noContent().build() : Response.status(Response.Status.NOT_FOUND).build(); }
Update a book
You can update a book by its _id value by adding the
following code to your resource class:
public Response updateBook( String id, Book book) { Book entity = bookRepository.findById(new ObjectId(id)); if (entity == null) { return Response.status(Response.Status.NOT_FOUND).build(); } entity.title = book.title; entity.author = book.author; entity.genre = book.genre; entity.year = book.year; bookRepository.update(entity); return Response.ok(entity).build(); }
Aggregate by genre
You can count books by genre by adding the following aggregation code to your resource class:
public Response countBooksByGenre() { List<Document> pipeline = new ArrayList<>(); pipeline.add(new Document("$group", new Document("_id", "$genre") .append("count", new Document("$sum", 1)))); List<Document> result = getCollection() .aggregate(pipeline) .into(new ArrayList<>()); return Response.ok(result).build(); }
Test the API
Start your application on http://localhost:8080 by running the
following command from the root directory of your project:
mvn spring-boot:run
You can test the API endpoints by running the following curl commands:
Insert one book
The following curl command shows how to insert a single book:
curl -X POST "http://localhost:8080/books" -H "Content-Type: application/json" -d '{ "title": "Quarkus in Action", "author": "John Doe", "genre": "Programming", "year": 2023 }'
Bulk insert
The following curl command shows how to insert multiple books:
curl -X POST http://localhost:8080/books/bulk \ -H "Content-Type: application/json" \ -d '[ { "title": "The Midnight Library", "author": "Matt Haig", "genre": "Fiction", "year": 2020 }, { "title": "Sapiens: A Brief History of Humankind", "author": "Yuval Noah Harari", "genre": "Non-Fiction", "year": 2011 } ]'
Find all books
The following curl command shows how to retrieve all books:
curl -X GET "http://localhost:8080/books" | jq
Find one book
The following curl command shows how to retrieve a specific book:
curl -X GET "http://localhost:8080/books/672f873b421eaa0c3e4da49f" | jq
Delete a book
The following curl command shows how to delete a book:
curl -X DELETE "http://localhost:8080/books/673f81b65750de0757a4bbfb" | jq
Update a book
The following curl command shows how to update a book:
curl -X PUT "http://localhost:8080/books/672f856f421eaa0c3e4da49e" \ -H "Content-Type: application/json" \ -d '{ "title": "Quarkus in Action", "author": "John Doe", "genre": "Programming", "year": 2021 }'
Aggregate by genre
The following curl command shows how to count books by genre:
curl -X GET "http://localhost:8080/books/aggregate/genre-count" | jq
Tip
Using jq to format JSON output
Use the jq tool, as shown in the preceding examples, to format the JSON
output from the API for better readability. You can install jq
by following the instructions on the jq website.
Additional Resources
To view the complete version of the example in this tutorial, see the quarkus-panache-with-mongodb Github repository.
To learn more about Panache, see the MongoDB with Panache documentation on the Quarkus website.