HowToDoInJava

  • Python
  • Java
  • Spring Boot
  • Dark Mode
Home / Spring Boot 2 / Spring Boot – CRUD Application

Spring Boot – CRUD Application

Learn to build Spring boot web applications supporting CRUD operations through form-based UI built on thymeleaf and spring mvc support.

1. Overview

In this tutorial, we are creating a web application with two views :

  • List all employees view – It display all the employees from database in a tabular format in UI. Additionally, there are links to update or delete any employee. This screen also has a separate option which can navigate to create employee screen.

    Spring boot hibernate thymeleaf example
    List all employees screen
  • Create/update employee view – This screen is used to add a new employee or edit the details of a existing employee.

    Add employee screen
    Add employee screen

There are two main components in this example to focus – MVC controller and UI views.

2. Spring MVC Controller

The controller class has URL mappings and it’s handler methods. There are handler methods are all CRUD operations including POST operation to handle form submission to create/update an employee.

Notice how given handler methods are binding model data to view; and they return view names in string format which gets resolved by view resolver in HTML files.

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.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import com.howtodoinjava.demo.entity.EmployeeEntity;
import com.howtodoinjava.demo.exception.RecordNotFoundException;
import com.howtodoinjava.demo.service.EmployeeService;

@Controller
@RequestMapping("/")
public class EmployeeMvcController 
{
	@Autowired
	EmployeeService service;

	@RequestMapping
	public String getAllEmployees(Model model) 
	{
		List<EmployeeEntity> list = service.getAllEmployees();

		model.addAttribute("employees", list);
		return "list-employees";
	}

	@RequestMapping(path = {"/edit", "/edit/{id}"})
	public String editEmployeeById(Model model, @PathVariable("id") Optional<Long> id) 
							throws RecordNotFoundException 
	{
		if (id.isPresent()) {
			EmployeeEntity entity = service.getEmployeeById(id.get());
			model.addAttribute("employee", entity);
		} else {
			model.addAttribute("employee", new EmployeeEntity());
		}
		return "add-edit-employee";
	}
	
	@RequestMapping(path = "/delete/{id}")
	public String deleteEmployeeById(Model model, @PathVariable("id") Long id) 
							throws RecordNotFoundException 
	{
		service.deleteEmployeeById(id);
		return "redirect:/";
	}

	@RequestMapping(path = "/createEmployee", method = RequestMethod.POST)
	public String createOrUpdateEmployee(EmployeeEntity employee) 
	{
		service.createOrUpdateEmployee(employee);
		return "redirect:/";
	}
}
  • getAllEmployees() – It returns list of all employees and is mapped to path “/”. It’s default view of the application.
  • editEmployeeById() – It is used to either add new or edit an existing employee. It uses the same HTML view for both operations. If there is an employee Id in context, then that employee is edited – else a new employee is created.
  • deleteEmployeeById() – A simple URL request to delete an employee by it’s Id.
  • createOrUpdateEmployee() – This method handle the HTTP POST requests which are used to either create a new employee or update an employee. Create or update operation depends on presence of employee id in model.

3. Thymeleaf templates

As stated before we are using two views in this example.

<!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 Employees</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css">
    <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.4.1/css/all.css">
</head>

<body>
    <div class="container my-2">
    <div class="card">
    <div class="card-body">
        <div th:switch="${employees}" class="container my-5">
            <p class="my-5">
                <a href="/edit" class="btn btn-primary">
                <i class="fas fa-user-plus ml-2"> Add Employee </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="employee : ${employees}">
                                <td th:text="${employee.firstName}"></td>
                                <td th:text="${employee.lastName}"></td>
                                <td th:text="${employee.email}"></td>
                                <td>
                                    <a th:href="@{/edit/{id}(id=${employee.id})}" 
                                             class="btn btn-primary">
                                        <i class="fas fa-user-edit ml-2"></i>
                                    </a>
                                </td>
                                <td>
                                    <a th:href="@{/delete/{id}(id=${employee.id})}" 
                                              class="btn btn-primary">
                                        <i class="fas fa-user-times ml-2"></i>
                                    </a>
                                </td>
                            </tr>
                        </tbody>
                    </table>
                </div>

            </div>
        </div>
    </div>
    </div>
    </div>
</body>

</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 Employee</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css">
    <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.4.1/css/all.css">
</head>

<body>
    <div class="container my-5">
        <h3> Add Employee</h3>
        <div class="card">
            <div class="card-body">
                <div class="col-md-10">
                    <form action="#" th:action="@{/createEmployee}" th:object="${employee}" 
                                                                       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-primary" value=" Submit ">
                            </div>

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

</html>

4. Entity and repository

We have bound the EmployeeEntity class as model to UI.

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

@Entity
@Table(name="TBL_EMPLOYEES")
public class EmployeeEntity {

    @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;
    
    //Setters and getters

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

To persist data in database, we are using H2 (in-memory) database and using Spring data’s CrudRepository interface. It provides out of the box in-built method for simple CRUD operations.

import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;

import com.howtodoinjava.demo.entity.EmployeeEntity;

@Repository
public interface EmployeeRepository 
			extends CrudRepository<EmployeeEntity, Long> {

}

Note that repository is initialized with two SQL files which create the database tables and populate default data to it.

DROP TABLE IF EXISTS TBL_EMPLOYEES;
 
CREATE TABLE TBL_EMPLOYEES (
  id INT AUTO_INCREMENT  PRIMARY KEY,
  first_name VARCHAR(250) NOT NULL,
  last_name VARCHAR(250) NOT NULL,
  email VARCHAR(250) DEFAULT NULL
);
INSERT INTO 
	TBL_EMPLOYEES (first_name, last_name, email) 
VALUES
  	('Lokesh', 'Gupta', 'howtodoinjava@gmail.com'),
  	('John', 'Doe', 'xyz@email.com');

5. Service class

Another important class to see is EmployeeService class through which controller interacts with repository. It contains additional business logic to perform.

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.howtodoinjava.demo.entity.EmployeeEntity;
import com.howtodoinjava.demo.exception.RecordNotFoundException;
import com.howtodoinjava.demo.repository.EmployeeRepository;

@Service
public class EmployeeService {
	
	@Autowired
	EmployeeRepository repository;
	
	public List<EmployeeEntity> getAllEmployees()
	{
		List<EmployeeEntity> result = (List<EmployeeEntity>) repository.findAll();
		
		if(result.size() > 0) {
			return result;
		} else {
			return new ArrayList<EmployeeEntity>();
		}
	}
	
	public EmployeeEntity getEmployeeById(Long id) throws RecordNotFoundException 
	{
		Optional<EmployeeEntity> employee = repository.findById(id);
		
		if(employee.isPresent()) {
			return employee.get();
		} else {
			throw new RecordNotFoundException("No employee record exist for given id");
		}
	}
	
	public EmployeeEntity createOrUpdateEmployee(EmployeeEntity entity) 
	{
		if(entity.getId()  == null) 
		{
			entity = repository.save(entity);
			
			return entity;
		} 
		else 
		{
			Optional<EmployeeEntity> employee = repository.findById(entity.getId());
			
			if(employee.isPresent()) 
			{
				EmployeeEntity newEntity = employee.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 deleteEmployeeById(Long id) throws RecordNotFoundException 
	{
		Optional<EmployeeEntity> employee = repository.findById(id);
		
		if(employee.isPresent()) 
		{
			repository.deleteById(id);
		} else {
			throw new RecordNotFoundException("No employee record exist for given id");
		}
	} 
}

6. Add Spring boot and Thymeleaf maven dependencies

In spring boot projects, we only need to add spring-boot-starter-thymeleaf dependency and project itself auto-configures thymeleaf with default configuration. It read the HTML templates from /src/main/resources/templates folder.

<?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.1.5.RELEASE</version>
		<relativePath /> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.howtodoinjava</groupId>
	<artifactId>demo</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>demo</name>
	<description>Demo project for Spring Boot</description>

	<properties>
		<java.version>1.8</java.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<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>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>
</project>

7. spring boot thymeleaf crud tutorial demo

Start this application as Spring boot application which starts the web application in the embedded tomcat server.

Hit the URL : http://localhost:8080/

Verify that screen is rendered with two default employee details from data.sql file.

Play with application. Create few new employees, edit existing employees. Delete some employees.

Do let me know if you face any error in above spring boot mvc example.

Sourcecode download

Happy Learning !!

Was this post helpful?

Let us know if you liked the post. That’s the only way we can improve.

Share this:

  • Twitter
  • Facebook
  • LinkedIn
  • Reddit

About Lokesh Gupta

A family guy with fun loving nature. Love computers, programming and solving everyday problems. Find me on Facebook and Twitter.

Feedback, Discussion and Comments

  1. Midhuna

    April 21, 2020

    hi,
    I am getting an error

    Whitelabel Error Page
    This application has no explicit mapping for /error, so you are seeing this as a fallback.

    Tue Apr 21 14:16:06 IST 2020
    There was an unexpected error (type=Not Found, status=404).
    No message available

    how to solve this

  2. Midhuna

    April 21, 2020

    Hi,
    I am getting an error while running the code as spring-boot application, Error is

    "----------------------
    java.lang.IllegalArgumentException: Sources must not be empty
    	at org.springframework.util.Assert.notEmpty(Assert.java:464) 
    ~[spring-core-5.1.7.RELEASE.jar:5.1.7.RELEASE]
    	at org.springframework.boot.SpringApplication.prepareContext
    (SpringApplication.java:391) [spring-boot-2.1.5.RELEASE.jar:2.1.5.RELEASE]
    	at org.springframework.boot.SpringApplication.run
    (SpringApplication.java:314) [spring-boot-2.1.5.RELEASE.jar:2.1.5.RELEASE]
    	at org.springframework.boot.SpringApplication.run
    (SpringApplication.java:1260) [spring-boot-2.1.5.RELEASE.jar:2.1.5.RELEASE]
    	at org.springframework.boot.SpringApplication.main
    (SpringApplication.java:1276) [spring-boot-2.1.5.RELEASE.jar:2.1.5.RELEASE]
    ---------------------------"
    

    I tried to lower the version to 2.1.4 ,but it didnt work. Can you help?

  3. Yvan

    December 24, 2019

    I don’t know why but the first time i launched it, it wasn’t working so i tried another one and that one worked. Then later I tried again and that other one wasn’t working but this one was working. I don’t know if i made an error somewhere I’m really confused can you help me please ?!!!

  4. komal

    November 12, 2019

    where is the database name?

  5. Ben

    October 28, 2019

    Do you have this in Github? Thanks!

    • Lokesh Gupta

      October 28, 2019

      Work in progress. https://github.com/lokeshgupta1981/spring-webmvc

Comments are closed on this article!

Search Tutorials

Spring Boot 2 Tutorial

  • Spring Boot – Introduction
  • Spring Boot – Starter parent
  • Spring Boot – Starter templates
  • Spring Boot – Multi-module project
  • Spring Boot – Annotations
  • Spring Boot – Auto configuration
  • Spring Boot – AOP
  • Spring Boot – Logging
  • Spring Boot – DevTools
  • Spring Boot – WAR Packaging
  • Spring Boot – REST API
  • Spring Boot – CRUD
  • Spring Boot – OAuth2
  • Spring Boot – Testing
  • Spring Boot – RestTemplate
  • Spring Boot – Thymeleaf
  • Spring Boot – Hibernate
  • Spring Boot – DataSource
  • Spring Boot – Error Handling
  • Spring Boot – Caching
  • Spring Boot – Retry
  • Spring Boot – BasicAuth
  • Spring Boot – H2 Database
  • Spring Boot – Ehcache 3.x
  • Spring Boot – Gson
  • Spring Boot – RMI
  • Spring Boot – Send Email
  • Spring Boot – Interview Questions

Spring Boot Tutorial

  • Spring Boot – CommandLineRunner
  • Spring Boot – Configure Jetty
  • Spring Boot – Tomcat Default Port
  • Spring Boot – Context Root
  • Spring Boot – SSL [https]
  • Spring Boot – Get all loaded beans
  • Spring Boot – PropertyEditor
  • Spring Boot – @EnableScheduling
  • Spring Boot – Jersey
  • Spring Boot – SOAP Webservice
  • Spring Boot – SOAP Client
  • Spring Boot – JMSTemplate
  • Spring Boot – REST APIs
  • Spring Boot – JSP View
  • Spring Boot – Actuator endpoints
  • Spring Boot – Role Based Security
  • Spring Boot – RSS / ATOM Feed
  • Spring Boot – Ehcache 2.x

Meta Links

  • About Me
  • Contact Us
  • Privacy policy
  • Advertise
  • Guest and Sponsored Posts

Recommended Reading

  • 10 Life Lessons
  • Secure Hash Algorithms
  • How Web Servers work?
  • How Java I/O Works Internally?
  • Best Way to Learn Java
  • Java Best Practices Guide
  • Microservices Tutorial
  • REST API Tutorial
  • How to Start New Blog

Copyright © 2020 · HowToDoInjava.com · All Rights Reserved. | Sitemap

  • Java 15 New Features
  • Sealed Classes and Interfaces
  • EdDSA (Ed25519 / Ed448)