Quarkus + FreeMarker + Hibernate - CRUD example

Hello everyone, Today we will learn how to develop a Quarkus CRUD web application, using FreeMarker template, H2DB, and Hibernate.


Following technologies stack being used:
  • Quarkus 2.2.3.Final
  • Hibernate 5.5.7.Final
  • Maven 3
  • JDK 11
  • FreeMarker 0.2.7
  • H2DB 
  • Bootstrap
Freemarker is a very popular and mature templating engine. Its integration as a Quarkus extension provides developers ease of configuration, and offers support for native images.

Project Structure:


If you want to use Freemarker you need to add the quarkiverse-freemarker extension. In your pom.xml file.

Dependency Management-Maven-Pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>com.knf.dev</groupId>
<artifactId>quarkus-freemarker-crud</artifactId>
<version>1.0</version>

<properties>
<compiler-plugin.version>3.8.1</compiler-plugin.version>
<maven.compiler.parameters>true</maven.compiler.parameters>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<quarkus-plugin.version>2.2.3.Final</quarkus-plugin.version>
<quarkus.platform.artifact-id>quarkus-universe-bom</quarkus.platform.artifact-id>
<quarkus.platform.group-id>io.quarkus</quarkus.platform.group-id>
<quarkus.platform.version>2.2.3.Final</quarkus.platform.version>
<surefire-plugin.version>2.22.1</surefire-plugin.version>
</properties>

<dependencyManagement>
<dependencies>
<dependency>
<groupId>${quarkus.platform.group-id}</groupId>
<artifactId>${quarkus.platform.artifact-id}</artifactId>
<version>${quarkus.platform.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

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

<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-hibernate-orm</artifactId>
</dependency>
<dependency>
<groupId>io.quarkiverse.freemarker</groupId>
<artifactId>quarkus-freemarker</artifactId>
<version>0.2.7</version>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-jdbc-h2</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>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-maven-plugin</artifactId>
<version>${quarkus-plugin.version}</version>
<executions>
<execution>
<goals>
<goal>build</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>${compiler-plugin.version}</version>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>${surefire-plugin.version}</version>
<configuration>
<systemProperties>
<java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager>
</systemProperties>
</configuration>
</plugin>
</plugins>
</build>

<profiles>
<profile>
<id>native</id>
<activation>
<property>
<name>native</name>
</property>
</activation>
<build>
<plugins>
<plugin>
<artifactId>maven-failsafe-plugin</artifactId>
<version>${surefire-plugin.version}</version>
<executions>
<execution>
<goals>
<goal>integration-test</goal>
<goal>verify</goal>
</goals>
<configuration>
<systemProperties>
<native.image.path>${project.build.directory}/${project.build.finalName}-runner</native.image.path>
</systemProperties>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<properties>
<quarkus.package.type>native</quarkus.package.type>
</properties>
</profile>
</profiles>
</project>

application.properties

# datasource configuration
quarkus.datasource.db-kind = h2
quarkus.datasource.username = sa
# quarkus.datasource.password =
quarkus.datasource.jdbc.url = jdbc:h2:mem:test

# drop and create the database at startup (use `update` to only update the schema)
quarkus.hibernate-orm.database.generation=drop-and-create

Create the Entity class

package com.knf.dev;

import javax.persistence.*;
import java.io.Serializable;

@Table(name = "users")
@Entity
public class User implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String firstName;
private String lastName;
private String email;


public User(Long id, String firstName, String lastName, String email) {
super();
this.id = id;
this.firstName = firstName;
this.lastName = lastName;
this.email = email;
}

public Long getId() {
return id;
}

public void setId(Long id) {
this.id = id;
}

public String getFirstName() {
return firstName;
}

public void setFirstName(String firstName) {
this.firstName = firstName;
}

public String getLastName() {
return lastName;
}

public void setLastName(String lastName) {
this.lastName = lastName;
}

public String getEmail() {
return email;
}

public void setEmail(String email) {
this.email = email;
}

public User() {
super();
}

}

Create CRUD Repository

package com.knf.dev;

import javax.inject.Inject;
import javax.inject.Singleton;
import javax.persistence.EntityManager;
import javax.transaction.Transactional;
import java.util.List;

@Singleton
public class UserResource {

@Inject
EntityManager entityManager;

public List<User> getUsers() {
return entityManager.createQuery("SELECT c FROM User c").getResultList();
}

public User getUser(Long id) {
return entityManager.find(User.class, id);
}

@Transactional(Transactional.TxType.REQUIRED)
public User addUser(User user) {
entityManager.persist(user);
return user;
}

@Transactional(Transactional.TxType.REQUIRED)
public void updateUser(User user) {
entityManager.merge(user);
}

@Transactional(Transactional.TxType.REQUIRED)
public void deleteUser(Long id) {
User user = getUser(id);
entityManager.remove(user);
}
}

Create the UserEndPoint class

package com.knf.dev;

import freemarker.template.Template;
import freemarker.template.TemplateException;
import io.quarkiverse.freemarker.TemplatePath;

import javax.inject.Inject;
import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;
import java.io.IOException;
import java.io.StringWriter;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Path("/")
public class UserEndpoint {

@Inject
@TemplatePath("home.ftlh")
Template home;

@Inject
@TemplatePath("create-update.ftlh")
Template creatUpdate;

@Inject
UserResource userResource;

@GET
@Produces(MediaType.TEXT_HTML)
public String getAllUserView()
throws TemplateException, IOException {
List<User> users = userResource.getUsers();
StringWriter stringWriter = new StringWriter();
home.process(Map.of("users", users), stringWriter);
return stringWriter.toString();
}

@GET
@Path("/create")
@Produces(MediaType.TEXT_HTML)
public String createUserView()
throws TemplateException, IOException {
User user = new User();
Map<String, Object> obj = new HashMap<>();
StringWriter stringWriter = new StringWriter();
obj.put("user", user);
obj.put("isUpdate", false);
creatUpdate.process(obj, stringWriter);
return stringWriter.toString();
}

@POST
@Produces(MediaType.TEXT_HTML)
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@Path("/create")
public String createUser
(@FormParam("firstName") String firstName,
@FormParam("lastName") String
lastName, @FormParam("email") String email)
throws TemplateException, IOException {
User usr = new User();
usr.setEmail(email);
usr.setFirstName(firstName);
usr.setLastName(lastName);
userResource.addUser(usr);
return getAllUserView();
}

@GET
@Produces(MediaType.TEXT_HTML)
@Path("/update/{id}")
public String updateUser(@PathParam("id") Long id)
throws TemplateException, IOException {
User user = userResource.getUser(id);
Map<String, Object> obj = new HashMap<>();
StringWriter stringWriter = new StringWriter();
obj.put("user", user);
obj.put("isUpdate", true);
creatUpdate.process(obj, stringWriter);
return stringWriter.toString();
}

@POST
@Path("/update/{id}")
@Produces(MediaType.TEXT_HTML)
public String createUser(@FormParam("firstName") String firstName,
@FormParam("lastName") String
lastName, @FormParam("email")
String email,
@PathParam("id") Long id)
throws TemplateException, IOException {
User usr = new User();
usr.setEmail(email);
usr.setFirstName(firstName);
usr.setLastName(lastName);
usr.setId(id);
userResource.updateUser(usr);
return getAllUserView();
}

@GET
@Produces(MediaType.TEXT_HTML)
@Path("/delete/{id}")
public String deleteUser(@PathParam("id") Long id)
throws TemplateException, IOException {
userResource.deleteUser(id);
return getAllUserView();
}
}

Creating FreeMarker templates

home.ftlh

<!doctype html>
<head>
<title>Freemarker Example</title>
<meta charset="utf-8">
<meta content="width=device-width, initial-scale=1" name="viewport">
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css"
rel="stylesheet">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js">
</script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/js/bootstrap.min.js">
</script>
</head>
<body>
<div class="container">
<h2>User CRUD operation with Quarkus + Freemarker Template</h2>
<a href="/create" class="btn btn-primary" role="button">Create New User</a>
<table class="table">
<thead >
<tr>
<th scope="col">Id</th>
<th scope="col">First Name</th>
<th scope="col">Last Name</th>
<th scope="col">Email</th>
<th scope="col"></th>
<th scope="col"></th>
</tr>
</thead>
<tbody>
<#list users as user>
<tr>
<th scope="row">${user.id}</th>
<td>${user.firstName}</td>
<td>${user.lastName}</td>
<td>${user.email}</td>
<td><a href="/update/${user.id}" class="btn btn-warning" role="button">
Update</a></td>
<td><a href="/delete/${user.id}" class="btn btn-danger" role="button">
Delete</a></td>
</tr>
</#list>
</tbody>
</table>
</div>
</body>
</html>

create-update.ftlh

<!doctype html>
<head>
<title>Freemarker Example</title>
<meta charset="utf-8">
<meta content="width=device-width, initial-scale=1" name="viewport">
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css"
rel="stylesheet">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js">
</script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/js/bootstrap.min.js">
</script>
</head>
<body>
<div class="container">
<h2>
<#if !isUpdate>Create</#if>
<#if isUpdate>Update</#if>
User
</h2>
<div>
<form action="<#if isUpdate>/update/${user.id}</#if><#if !isUpdate>/create</#if>"
method="post" name="user">
<table class="table">
<tbody>
<thead>
<tr>
<th>Field</th>
<th>Value</th>
</tr>
</thead>
<tbody>
<#if isUpdate>
<tr>
<th>ID</th>
<td>
<div name="id">${user.id}</div>
</td>
</tr>
</#if>
<tr>
<th>First Name</th>
<td><input name="firstName" type="text"
value="<#if user.firstName??>${user.firstName}</#if>"/>
</td>
</tr>
<tr>
<th>Last Name</th>
<td><input name="lastName" type="text"
value="<#if user.lastName??>${user.lastName}</#if>"/></td>
</tr>
<tr>
<th>Email</th>
<td><input name="email" type="text"
value="<#if user.email??>${user.email}</#if>"/></td>
</tr>
</tbody>
</table>
<button class="btn btn-primary" type="submit">Save</button>
</form>
</div>
</div>
</body>
</html>

Popular posts from this blog

Learn Java 8 streams with an example - print odd/even numbers from Array and List

Java Stream API - How to convert List of objects to another List of objects using Java streams?

Registration and Login with Spring Boot + Spring Security + Thymeleaf

Java, Spring Boot Mini Project - Library Management System - Download

ReactJS, Spring Boot JWT Authentication Example

Spring Boot + Mockito simple application with 100% code coverage

Top 5 Java ORM tools - 2024

Java - Blowfish Encryption and decryption Example

Spring boot video streaming example-HTML5

Google Cloud Storage + Spring Boot - File Upload, Download, and Delete