Tagged: Thymeleaf

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.
  • Create/update employee view – This screen is used to add a new employee or edit the details of a existing .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.fusebes.demo.entity.EmployeeEntity;import com.fusebes.demo.exception.RecordNotFoundException;import com.fusebes.demo.service.EmployeeService; @Controller@RequestMapping("/")public class EmployeeMvcController {@AutowiredEmployeeService service; @RequestMappingpublic 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 @Overridepublic 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.fusebes.demo.entity.EmployeeEntity; @Repositorypublic 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.

INSERT INTO TBL_EMPLOYEES (first_name, last_name, email) VALUES('Lokesh', 'Gupta', 'fusebes@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.fusebes.demo.entity.EmployeeEntity;import com.fusebesemo.exception.RecordNotFoundException;import com.fusebes.demo.repository.EmployeeRepository; @Servicepublic class EmployeeService { @AutowiredEmployeeRepository 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.fusebes</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.

Server Side Templating in Spring Boot using Thymeleaf

Server Side Templating in Spring Boot using Thymeleaf

Thymeleaf is a serve-side template engine for Java. It has built-in support for Spring framework and is widely used in Spring based Projects.

In fact, Spring Boot itself has been promoting thymeleaf via several thymeleaf based projects and examples in its blog.

The purpose of this blog post is to build a Simple application with thymeleaf and Spring Boot, and learn all the details along the way.

Creating the Project

We’ll use Spring Initializr web tool to create our project. It is by far the simplest tool to generate a Spring Boot application.

Head over to http://start.spring.io and generate a project with the following details –

  • Group : com.example
  • Artifact : thymeleaf-tour
  • Dependencies : Web, Thymeleaf, DevTools
Spring Boot Thymeleaf Template Engine Example Tutorial

After generating the project, you must have got your project’s zip file. Unzip the file and import it in your favorite IDE.

The project’s directory structure looks like this –

Spring Boot Thymeleaf Template Engine Sample Directory Structure

Running the Application

You can run the application using the following command –

$ mvn spring-boot:run

The application will start on spring boot’s default tomcat port 8080. If you browse http://localhost:8080 on your web browser, the app will respond with a 404 error page because we haven’t created any server endpoint yet.

Let’s do that now.

Defining a Controller

First, Create a new package controller inside com.example.thymeleaftour package, and then create a new file HomeController with the following code –

package com.example.thymeleaftour.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;

public class HomeController {

    private static final String appName = "ThymeleafTour";

    public String home(Model model,
                       @RequestParam(value = "name", required = false,
                               defaultValue = "Guest") String name) {

        model.addAttribute("name", name);
        model.addAttribute("title", appName);
        return "home";


In the above controller, We have defined a Request Parameter called name. This is an optional parameter with a default value of Guest.

Whenever a user requests the home page, we’ll display the application name and user’s name if it is supplied in the request parameter, or we’ll just show Hello Guest!

Next, We’re adding the name and title attributes to the Model so that they can be accessed from the template.

Finally, we’re returning the template name which will be used to render the response to the browser.

Creating a Thymeleaf Template

All Server side templates go into src/main/resources/templates directory. Create a new file called home.html inside the templates directory with the following contents –

<html xmlns:th="http://www.thymeleaf.org">
    <title th:text="${title}"></title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <div class="page-content">
        <h1 th:text="|Hello ${name}!|"></h1>
        <h2 th:text="|Welcome to ${title} application|"></h2>

Notice the use of th:text attributes in the template above. It is a thymeleaf attribute, which evaluates the expression present in the value and sets the result as the body of the host tag.

The value "|Hello ${name}!|" in the template, is same as "'Hello' + ${name}". It’s just a syntactic sugar provided by thymeleaf for writing mixed expressions.

Adding static resources

Let’s add some css to our template. All static files in Spring Boot go to src/main/resources/static folder. Create a new folder css inside the static folder and then create a file main.css inside static/css.

Add the following styles to your css file –

body {
    background: #43cea2;
    background: -webkit-linear-gradient(to right, #185a9d, #43cea2); 
    background: linear-gradient(to right, #185a9d, #43cea2);
    color: #fff;
    text-align: center;

.page-content {
    position: absolute;
    text-align: center;
    left: 0;
    right: 0;
    top: 35%;
    bottom: 0;

h1 {
    font-size: 46px;
    margin-top: 10px;
    margin-bottom: 10px;

h2 {
    font-size: 34px;
    margin-top: 10px;
    margin-bottom: 10px;

Finally, We need to reference the css file inside home.html. Just add the following link tag in the head section of home.html file –

<link rel="stylesheet" href="/css/main.css" />

Running the application

You can run the application by going to the app’s root directory and typing the following command –

$ mvn spring-boot:run

You can browse the application at localhost:8080. Following is a screenshot of the app we just built –

Spring Boot Thymeleaf Example

Also, If you pass your name in the request parameter – http://localhost:8080?name=yaniv, then the app will greet you with your name instead of displaying Hello Guest.


In this tutorial, we learned how to use thymeleaf with Spring Boot. You can learn more about thymeleaf’s syntax and features from thymeleaf’s documentation.

Thank you for reading. Please ask any questions in the comment section below.