Spring Boot + FreeMarker + Spring Data JPA - CRUD example

Hello everyone, Today we will learn how to develop a Spring Boot CRUD web application, using Spring Boot, FreeMarker template, H2DB, and JPA.
Apache FreeMarker is a template engine: a Java library to generate text output (HTML web pages, e-mails, configuration files, source code, etc.) based on templates and changing data. Templates are written in the FreeMarker Template Language (FTL), which is a simple, specialized language. Usually, a general-purpose programming language (like Java) is used to prepare the data (issue database queries, do business calculations). Then, Apache FreeMarker displays that prepared data using templates. In the template, you are focusing on how to present the data, and outside the template, you are focusing on what data to present. 


The technology stack used:

  • Spring Boot 2.5.5
  • Spring MVC 5.3.10
  • Maven 3
  • JDK 11
  • FreeMarker 2.3.31
  • H2DB 
  • Bootstrap

Project Structure



Dependency Management-Maven-Pom.xml

Puts spring-boot-starter-freemarker, spring-boot-starter-jpa, h2 dependencies
<?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
https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.5</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.knf.dev.demo</groupId>
<artifactId>springbootfreemarkerjpacrud</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springbootfreemarkerjpacrud</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>11</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>

</project>


Create the User Entity

package com.knf.dev.demo.springbootfreemarkerjpacrud.model;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name = "user")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "first_name")
private String firstName;
@Column(name = "last_name")
private String lastName;
@Column(name = "email", nullable = false, length = 200)
private String email;

public User() {
super();
}

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(String firstName, String lastName, String email) {
super();
this.firstName = firstName;
this.lastName = lastName;
this.email = email;
}

@Override
public String toString() {
return "User [id=" + id + ", firstName=" + firstName + ", lastName="
+ lastName + ", email=" + email + "]";
}
}


Create CRUD Repository

package com.knf.dev.demo.springbootfreemarkerjpacrud.repository;

import com.knf.dev.demo.springbootfreemarkerjpacrud.model.User;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface UserRepository extends CrudRepository<User, Long> {

}


Creat RecordNotFoundException

package com.knf.dev.demo.springbootfreemarkerjpacrud.exception;

import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;

@ResponseStatus(value = HttpStatus.NOT_FOUND)
public class RecordNotFoundException extends Exception {

private static final long serialVersionUID = 1L;

public RecordNotFoundException(String message) {
super(message);
}

public RecordNotFoundException(String message, Throwable t) {
super(message, t);
}
}


Create the User Service 

package com.knf.dev.demo.springbootfreemarkerjpacrud.service;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import com.knf.dev.demo.springbootfreemarkerjpacrud.exception.RecordNotFoundException;
import com.knf.dev.demo.springbootfreemarkerjpacrud.model.User;
import com.knf.dev.demo.springbootfreemarkerjpacrud.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class UserService {
@Autowired
UserRepository repository;

public List<User> getAllusers() {
List<User> result = (List<User>) repository.findAll();
if (result.size() > 0) {
return result;
} else {
return new ArrayList<User>();
}
}

public User getUserById(Long id) throws RecordNotFoundException {
Optional<User> user = repository.findById(id);
if (user.isPresent()) {
return user.get();
} else {
throw new RecordNotFoundException
("No user record exist for given id");
}
}

public User createOrUpdateUser(User entity) {
if (entity.getId() == null) {
entity = repository.save(entity);
return entity;
} else {
Optional<User> user = repository.findById(entity.getId());
if (user.isPresent()) {
User newEntity = user.get();
newEntity.setEmail(entity.getEmail());
newEntity.setFirstName(entity.getFirstName());
newEntity.setLastName(entity.getLastName());
newEntity = repository.save(newEntity);
return newEntity;
} else {
entity = repository.save(entity);
return entity;
}
}
}

public void deleteUserById(Long id) throws RecordNotFoundException {
Optional<User> user = repository.findById(id);
if (user.isPresent()) {
repository.deleteById(id);
} else {
throw new RecordNotFoundException
("No user record exist for given id");
}
}
}



Create the Web Controller  

package com.knf.dev.demo.springbootfreemarkerjpacrud.controller;

import com.knf.dev.demo.springbootfreemarkerjpacrud.exception.RecordNotFoundException;
import com.knf.dev.demo.springbootfreemarkerjpacrud.model.User;
import com.knf.dev.demo.springbootfreemarkerjpacrud.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import java.util.List;

@Controller
public class UserController {

@Autowired
private UserService userService;
@GetMapping("/")
public String getAllUserView(Model model) {
List<User> users = userService.getAllusers();
model.addAttribute("users", users);
return "home";
}
@GetMapping("/create")
public String createUserView(Model model) {
User user = new User();
model.addAttribute("user", user);
model.addAttribute("isUpdate", false);
return "create-update";
}
@PostMapping("/update/{id}")
public String createUser(@ModelAttribute("user") User user,
@PathVariable("id") Long id) {
user.setId(id);
userService.createOrUpdateUser(user);
return "redirect:/";
}
@GetMapping("/update/{id}")
public String updateUser(Model model, @PathVariable("id") Long id)
throws RecordNotFoundException {
User user = userService.getUserById(id);
model.addAttribute("user", user);
model.addAttribute("isUpdate", true);
return "create-update";
}
@PostMapping("/create")
public String createUser(@ModelAttribute("user") User user) {
userService.createOrUpdateUser(user);
return "redirect:/";
}
@GetMapping("/delete/{id}")
public String deleteUser(@PathVariable("id") Long id)
throws RecordNotFoundException {
userService.deleteUserById(id);
return "redirect:/";
}
}


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 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>


Spring Boot Main Driver

package com.knf.dev.demo.springbootfreemarkerjpacrud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class SpringbootfreemarkerjpacrudApplication {

public static void main(String[] args) {
SpringApplication.run(SpringbootfreemarkerjpacrudApplication.class, args);
}
}


Local Setup and Run the application

Step1: Download or clone the source code from GitHub to the local machine - Click here


Backend

Step 2: mvn clean install

Step 3: Run the Spring Boot application - mvn spring-boot:run

Access the URL in  the browser: http://localhost:8080/

Create User:


List all Users:


Update User:



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