Spring Boot + Thymeleaf + Spring Data JPA CRUD Example

Hello everyone, In this post we will learn how to develop a  CRUD web application, using Spring Boot, Thymeleaf, Spring Data JPA and H2 Database.



Technologies Used:

  • Java 17
  • Spring Boot 2.7.0
  • Spring Data JPA
  • Thymeleaf template engine
  • Maven 3+
  • Bootstrap 4
  • H2 Database

Project Directory:



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>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.0</version>
<relativePath />
</parent>
<artifactId>springboot-thymeleaf-jpa-crud</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springboot-thymeleaf-jpa-crud</name>
<groupId>springboot-thymeleaf-jpa-crud</groupId>
<properties>
<java.version>17</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-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
</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>

<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<description>Spring Boot + Thymeleaf CRUD</description>
</project>


Create User Entity

package com.knowledgefactory.entity;

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 = "UserTable")
public class User {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@Column(name = "firstName")
private String firstName;

@Column(name = "lastName")
private String lastName;

@Column(name = "email", nullable = false, length = 200)
private String email;

public User() {

}

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


Create Custom Exception

package com.knowledgefactory.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 throwable) {
super(message, throwable);
}
}


Create User Repository

package com.knowledgefactory.repository;

import java.util.List;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
import com.knowledgefactory.entity.User;

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

List<User> findAll();
}


Create UserService

package com.knowledgefactory.service;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.knowledgefactory.entity.User;
import com.knowledgefactory.exception.RecordNotFoundException;
import com.knowledgefactory.repository.UserRepository;

@Service
public class UserService {

@Autowired
UserRepository repository;

public List<User> getAllusers() {

List<User> result = 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 UserController

package com.knowledgefactory.controller;

import java.util.List;
import java.util.Optional;
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.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import com.knowledgefactory.entity.User;
import com.knowledgefactory.exception.RecordNotFoundException;
import com.knowledgefactory.service.UserService;

@Controller
public class UserController {

@Autowired
UserService service;

@GetMapping({ "/" })
public String getAllUsers(Model model) {

List<User> list = service.getAllusers();
model.addAttribute("users", list);
return "list-users";
}

@GetMapping(path = { "/addOrEdit", "/addOrEdit/{id}" })
public String editUserById(Model model,
@PathVariable("id") Optional<Long> id)
throws RecordNotFoundException {

if (id.isPresent()) {
User entity = service.getUserById(id.get());
model.addAttribute("user", entity);
} else {
model.addAttribute("user", new User());
}
return "add-edit-user";
}

@GetMapping(path = "/delete/{id}")
public String deleteUserById(Model model,
@PathVariable("id") Long id)
throws RecordNotFoundException {

service.deleteUserById(id);
return "redirect:/";
}

@PostMapping(path = "/createOrUpdateUser")
public String createOrUpdateUser(User user) {

service.createOrUpdateUser(user);
return "redirect:/";
}
}



Spring Boot Main Driver

package com.knowledgefactory;

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

@SpringBootApplication
public class Application {

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


View

add-edit-user.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">

<head>
<meta charset="utf-8">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<title>Add User</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet"
href="https://use.fontawesome.com/releases/v5.4.1/css/all.css">
<link rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/css/bootstrap.min.css">
<script
src="https://cdn.jsdelivr.net/npm/jquery@3.6.0/dist/jquery.slim.min.js">
</script>
<script
src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/js/bootstrap.bundle.min.js">
</script>
</head>

<body>
<div class="container my-5">
<h3 th:if="${user.id != null}">Edit User</h3>
<h3 th:if="${user.id == null}">Add User</h3>
<div class="card">
<div class="card-body">
<div class="col-md-10">
<form action="#" th:action="@{/createOrUpdateUser}"
th:object="${user}" method="post">
<div class="row">
<div class="form-group col-md-8">
<label for="name" class="col-form-label"> First Name
</label>
<input type="text" th:field="*{firstName}"
class="form-control" id="firstName"
placeholder="First Name" />
</div>

<div class="form-group col-md-8">
<label for="name" class="col-form-label"> Last Name
</label>
<input type="text" th:field="*{lastName}"
class="form-control"
id="lastName" placeholder="Last Name" />
</div>

<div class="form-group col-md-8">
<label for="email" class="col-form-label"> Email
</label>
<input type="text" th:field="*{email}"
class="form-control" id="email"
placeholder="Email Id" />
</div>

<div class="col-md-6">
<input type="submit" class="btn btn-success"
value=" Submit ">
</div>

<input type="hidden" id="id" th:field="*{id}">
</div>
</form>
</div>
</div>
</div>
</div>
</body>
</html>


list-users.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">

<head>
<meta charset="utf-8">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<title>All Users</title>
<meta name="viewport" content="width=device-width, initial-scale=1">

<link rel="stylesheet"
href="https://use.fontawesome.com/releases/v5.4.1/css/all.css">
<link rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/css/bootstrap.min.css">
<script
src="https://cdn.jsdelivr.net/npm/jquery@3.6.0/dist/jquery.slim.min.js">
</script>
<script
src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/js/bootstrap.bundle.min.js">
</script>

</head>

<body>
<div class="container my-2">
<div class="card">
<div class="card-body">
<div th:switch="${users}" class="container my-5">
<p class="my-5">
<a href="/addOrEdit" class="btn btn-success">
<i class="fas fa-user-plus ml-2"> Add User </i>
</a>
</p>
<div class="col-md-10">
<h2 th:case="null">No record found !!</h2>
<div th:case="*">
<table class="table table-striped table-responsive-md">
<thead>
<tr>
<th>First Name</th>
<th>Last Name</th>
<th>Email</th>
<th>Edit</th>
<th>Delete</th>
</tr>
</thead>
<tbody>
<tr th:each="user : ${users}">
<td th:text="${user.firstName}"></td>
<td th:text="${user.lastName}"></td>
<td th:text="${user.email}"></td>
<td><a th:href="@{/addOrEdit/{id}(id=${user.id})}"
class="btn btn-warning">
<i class="fas fa-user-edit ml-2"></i>
</a></td>
<td><a th:href="@{/delete/{id}(id=${user.id})}"
class="btn btn-danger">
<i class="fas fa-user-times ml-2"></i>
</a></td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
</body>
</html>


Download the complete source code - click here                                     

Local Setup and Run the application


Step 1: Download or clone the source code from GitHub to a local machine - Click here


Step 2: mvn clean install


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


From the browser call the endpoint http://localhost:8080/

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

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