Making a Spring Boot & Thymeleaf CRUD Application

Overview

In this lesson, we will demonstrate how we can use Thymeleaf in a Spring Boot based application to show data in a simple template frontend. We will be using the Spring Initializr tool for setting up the project quickly. We will also mention the dependencies present in our project if you choose to setup the project manually. You can find full source code here.

Setting up the project

We will make use of Spring Initializr tool for quickly setting up the project. We will use following dependencies:

Download the project and unzip it.

Maven Dependencies

Though we already completed the setup with the tool, if you want to set it up manually, we use Maven build system for this project and here are the dependencies we used:

<?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.javabeginnerstutorial</groupId>
<artifactId>boot-hibernate-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>

<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.10.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</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-web</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>

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

Make sure to use stable version for Spring Boot from the maven central.

Project Structure

By the end of this lesson, this is the project structure which we will be having:

Keep the focus on the package structure. To directly use the application, we will use it with a command line runner.

Defining the Model

To save an object into the H2 in-memory database, we define a Person model object with basic fields:

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

@Entity
public class Person {

@Id
@GeneratedValue
private Long id;
private String name;
private int age;

//standard getters and setters
}

It is a standard POJO with getters and setters.

Defining JPA Repository

Let us use simple JPA interface to add database interaction capability into our project:

import com.javabeginnerstutorial.bootdemo.model.Person;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface PersonRepository extends JpaRepository<Person, Long> { }

This is empty as all the functionality we need is already fulfilled by the default JPA methods. We will explore them in coming section.

Defining the Service

We will now define a simple service layer which will server Controller layer. In this layer, we will first define some contract methods in an interface. Let’s see the code snippet here:

import com.javabeginnerstutorial.bootdemo.model.Person;
import java.util.List;

public interface PersonService {
Person createPerson(Person person);
Person getPerson(Long id);
Person editPerson(Person person);
void deletePerson(Person person);
void deletePerson(Long id);
List<Person> getAllPersons(int pageNumber, int pageSiz);
List<Person> getAllPersons();
}

This interface just defines some contract methods the service layer must implement. Let’s move to the implementation now.

Providing Service Implementation

Let’s implement the methods defined above using the JPA Repository bean:

import com.javabeginnerstutorial.bootdemo.model.Person;
import com.javabeginnerstutorial.bootdemo.repository.PersonRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class PersonServiceImpl implements PersonService {

@Autowired
private PersonRepository personRepository;

@Override
public Person createPerson(Person person) {
return personRepository.save(person);
}

@Override
public Person getPerson(Long id) {
return personRepository.findOne(id);
}

@Override
public Person editPerson(Person person) {
return personRepository.save(person);
}

@Override
public void deletePerson(Person person) {
personRepository.delete(person);
}

@Override
public void deletePerson(Long id) {
personRepository.delete(id);
}

@Override
public List<Person> getAllPersons(int pageNumber, int pageSize) {
return personRepository.findAll(new PageRequest(pageNumber, pageSize)).getContent();
}

@Override
public List<Person> getAllPersons() {
return personRepository.findAll();
}
}

All the methods were actually having a single line of body. This was made possible by the JPA Repository interface itself as we were able to use the methods defined in the repository interface.

Defining Templates

We will now define the Thymeleaf based templates for our two pages. First page will be a form where we can enter data which needs to be saved to the database.

The page, when constructed will look like:

The page HTML will look like:

<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Spring Boot: javabeginnerstutorial.com</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
</head>
<body>
<h1>New Person</h1>
<form action="#" th:action="@{/person}" th:object="${person}" method="post">
<p>Name: <input type="text" th:field="*{name}"/></p>
<p>Age: <input type="number" th:field="*{age}"/></p>
<p><input type="submit" value="Submit"/> <input type="reset" value="Reset"/></p>
</form>
</body>
</html>

Next, we will design a page where we can see all data. The page, when constructed will look like:

The page HTML will look like:

<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Spring Boot: javabeginnerstutorial.com</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
</head>
<body>
<div th:if="${not #lists.isEmpty(persons)}">
<h2>Person List</h2>
<table class="glyphicon glyphicon-calendar">
<tr>
<th>Id</th>
<th>Name</th>
<th>Age</th>
</tr>
<tr th:each="person : ${persons}">
<td th:text="${person.id}"></td>
<td th:text="${person.name}"></td>
<td th:text="${person.Age}"></td>
</tr>
</table>
</div>
</body>
</html>

Finally, let us make a controller which provides API endpoints.

Defining Controller

The Controller will have request mappings. First to present the user the form where he can enter data.

Second mapping will present page with all data present. Let’s see our Controller now:

import com.javabeginnerstutorial.bootdemo.model.Person;
import com.javabeginnerstutorial.bootdemo.service.PersonService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@Controller
public class PersonController {

private final PersonService personService;

@Autowired
public PersonController(PersonService personService) {
this.personService = personService;
}

@RequestMapping(value = "", method = RequestMethod.GET)
public String homePage(Model model) {
model.addAttribute("person", new Person());
return "greeting";
}

@RequestMapping(value = "/person", method = RequestMethod.POST)
public String addPagePerson(@ModelAttribute Person person, Model model) {
personService.createPerson(person);
model.addAttribute("persons", personService.getAllPersons());
return "result";
}
}

With Thymeleaf on our classpath, Spring Boot detects it and infers the String we return as the name for HTML Thymeleaf templates and pick them from the resource directory.

Conclusion

In this lesson, we looked at how much ease Spring Boot provides us with quick project setup and how we can integrate Thymeleaf with minimal and yet powerful configuration. Feel free to leave comments below.

Making a Spring Boot & Thymeleaf CRUD Application

Overview

In this lesson, we will demonstrate how we can use Thymeleaf in a Spring Boot based application to show data in a simple template frontend. We will be using the Spring Initializr tool for setting up the project quickly. We will also mention the dependencies present in our project if you choose to setup the project manually.

Setting up the project

We will make use of Spring Initializr tool for quickly setting up the project. We will use following dependencies:

Download the project and unzip it.

Maven Dependencies

Though we already completed the setup with the tool, if you want to set it up manually, we use Maven build system for this project and here are the dependencies we used:

<?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.javabeginnerstutorial</groupId>
<artifactId>boot-hibernate-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>

<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.10.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</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-web</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>

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

Make sure to use stable version for Spring Boot from the maven central.

Project Structure

By the end of this lesson, this is the project structure which we will be having:

Keep the focus on the package structure. To directly use the application, we will use it with a command line runner.

Defining the Model

To save an object into the H2 in-memory database, we define a Person model object with basic fields:

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

@Entity
public class Person {

@Id
@GeneratedValue
private Long id;
private String name;
private int age;

//standard getters and setters
}

It is a standard POJO with getters and setters.

Defining JPA Repository

Let us use simple JPA interface to add database interaction capability into our project:

import com.javabeginnerstutorial.bootdemo.model.Person;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface PersonRepository extends JpaRepository<Person, Long> { }

This is empty as all the functionality we need is already fulfilled by the default JPA methods. We will explore them in coming section.

Defining the Service

We will now define a simple service layer which will server Controller layer. In this layer, we will first define some contract methods in an interface. Let’s see the code snippet here:

import com.javabeginnerstutorial.bootdemo.model.Person;
import java.util.List;

public interface PersonService {
Person createPerson(Person person);
Person getPerson(Long id);
Person editPerson(Person person);
void deletePerson(Person person);
void deletePerson(Long id);
List<Person> getAllPersons(int pageNumber, int pageSiz);
List<Person> getAllPersons();
}

This interface just defines some contract methods the service layer must implement. Let’s move to the implementation now.

Providing Service Implementation

Let’s implement the methods defined above using the JPA Repository bean:

import com.javabeginnerstutorial.bootdemo.model.Person;
import com.javabeginnerstutorial.bootdemo.repository.PersonRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class PersonServiceImpl implements PersonService {

@Autowired
private PersonRepository personRepository;

@Override
public Person createPerson(Person person) {
return personRepository.save(person);
}

@Override
public Person getPerson(Long id) {
return personRepository.findOne(id);
}

@Override
public Person editPerson(Person person) {
return personRepository.save(person);
}

@Override
public void deletePerson(Person person) {
personRepository.delete(person);
}

@Override
public void deletePerson(Long id) {
personRepository.delete(id);
}

@Override
public List<Person> getAllPersons(int pageNumber, int pageSize) {
return personRepository.findAll(new PageRequest(pageNumber, pageSize)).getContent();
}

@Override
public List<Person> getAllPersons() {
return personRepository.findAll();
}
}

All the methods were actually having a single line of body. This was made possible by the JPA Repository interface itself as we were able to use the methods defined in the repository interface.

Defining Templates

We will now define the Thymeleaf based templates for our two pages. First page will be a form where we can enter data which needs to be saved to the database.

The page, when constructed will look like:

The page HTML will look like:

<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Spring Boot: javabeginnerstutorial.com</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
</head>
<body>
<h1>New Person</h1>
<form action="#" th:action="@{/person}" th:object="${person}" method="post">
<p>Name: <input type="text" th:field="*{name}"/></p>
<p>Age: <input type="number" th:field="*{age}"/></p>
<p><input type="submit" value="Submit"/> <input type="reset" value="Reset"/></p>
</form>
</body>
</html>

Next, we will design a page where we can see all data. The page, when constructed will look like:

The page HTML will look like:

<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Spring Boot: javabeginnerstutorial.com</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
</head>
<body>
<div th:if="${not #lists.isEmpty(persons)}">
<h2>Person List</h2>
<table class="glyphicon glyphicon-calendar">
<tr>
<th>Id</th>
<th>Name</th>
<th>Age</th>
</tr>
<tr th:each="person : ${persons}">
<td th:text="${person.id}"></td>
<td th:text="${person.name}"></td>
<td th:text="${person.Age}"></td>
</tr>
</table>
</div>
</body>
</html>

Finally, let us make a controller which provides API endpoints.

Defining Controller

The Controller will have request mappings. First to present the user the form where he can enter data.

Second mapping will present page with all data present. Let’s see our Controller now:

import com.javabeginnerstutorial.bootdemo.model.Person;
import com.javabeginnerstutorial.bootdemo.service.PersonService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@Controller
public class PersonController {

private final PersonService personService;

@Autowired
public PersonController(PersonService personService) {
this.personService = personService;
}

@RequestMapping(value = "", method = RequestMethod.GET)
public String homePage(Model model) {
model.addAttribute("person", new Person());
return "greeting";
}

@RequestMapping(value = "/person", method = RequestMethod.POST)
public String addPagePerson(@ModelAttribute Person person, Model model) {
personService.createPerson(person);
model.addAttribute("persons", personService.getAllPersons());
return "result";
}
}

With Thymeleaf on our classpath, Spring Boot detects it and infers the String we return as the name for HTML Thymeleaf templates and pick them from the resource directory.

Conclusion

In this lesson, we looked at how much ease Spring Boot provides us with quick project setup and how we can integrate Thymeleaf with minimal and yet powerful configuration. Feel free to leave comments below.

Leave A Comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.