Spring Boot Search Example using Thymeleaf and Spring Data JPA

Spring Boot Search Example using Thymeleaf and Spring Data JPA. In this article, we will learn how to filter data using the thymeleaf template and Spring data JPA.

We will create a spring boot application where we will implement search or filter functionality using Spring Data JPA at the data access layer, Thymeleaf at the view layer, and MYSQL as a database.

Steps for Spring Boot Search Example using Thymeleaf and Spring Data JPA

  1. Create a Spring Boot Starter Project.
  2. Create a package structure
  3. Create a database in MYSQL
  4. Configure application. properties file.
  5. Create an Entity
  6. Create a Repository i.e the Data Access Layer.
  7. Create Service Layer.
  8. Create Controller.
  9. Create Views using Thymeleaf.

Spring Boot Search Example using Thymeleaf and Spring Data JPA

In the below example, let’s implement simple search functionality in the spring boot application using the thymeleaf template and Spring Data JPA at the data access layer.

Create a Project

Step 1: Open IDE STS- Spring Tool Suite

Step 2: Go to File > Spring Starter Project.

Step 3: Now, Fill all the fields as shown below and click Next.

Step 4: Now, Add the dependencies as per your requirement, I have added Spring Web Dependency and Spring Data JPA, Thymeleaf, and etc. click Next > Finish.

Now, wait for some time and your project structure will be ready. Go to the pom.xml file and you will see the following dependencies will be added automatically.

<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>mysql</groupId>
   <artifactId>mysql-connector-java</artifactId>
   <scope>runtime</scope>
  </dependency>
  <dependency>
   <groupId>org.projectlombok</groupId>
   <artifactId>lombok</artifactId>
   <optional>true</optional>
  </dependency>
  <dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-test</artifactId>
   <scope>test</scope>
  </dependency>
 </dependencies>

Once the project is created, create the folder structure as shown below

Create Database

mysql> create database db_demo;

Configure application. properties file

This is the file provided by spring boot to configure JDBC URL, username, password, and driver class name. Also, configure JPA-related information.

# change the port
server.port=8888
#Database Configrations
spring.datasource.url=jdbc:mysql://localhost:3306/db_demo
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.username=root
spring.datasource.password=root
spring.jpa.database-platform = org.hibernate.dialect.MySQL8Dialect
spring.jpa.generate-ddl=true
spring.jpa.hibernate.ddl-auto = update
  • spring.jpa.hibernate.ddl-auto is set to update so that whatever changes we do would be reflected in the schema.
  • spring.datasource.url is used to set the URL of the MYSQL DB.
  • spring.datasource.username is used to set the username and spring. datasource. password is used to set the password.
  • spring.datasource.driver-class-name is used to set the driver class name.
  • spring.jpa.show-sql is set to true to show SQL generated by the Hibernate.
  • spring.jpa.properties.hibernate.dialect is used to generate better SQL for the chosen database.
  • spring.jpa.properties.hibernate.format_sql is set to true to format SQL queries.
  • server.port is set to 8888.

Create a Model Class

Here, we will create an Entity that would be mapped to the database tables. It is nothing but the Java POJO class.

Shop.java

package com.example.thymeleaf.model;

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

import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

@Setter
@Getter
@ToString
@Entity
@Table(name = "shop")
public class Shop {

 @Id
 @GeneratedValue(strategy = GenerationType.IDENTITY)
 private Integer id;
 private String ownerName;
 private long shopNo;
 private String shopType;
 private String address;
 private String country;
}
  • @Entity is used to annotate the classes to indicate that they are JPA entities.
  • @Table annotation is used to specify the name of the table that should be mapped with entities.
  • @Id annotation is used for the primary key.
  • I have used the Lombok library to remove boilerplate code. In case you want to know what is Lombok check this article https://codedec.com/tutorials/how-to-configure-lombok-into-eclipse/

Create Repository Interface

The repository here is the DAO layer, which performs all the database operations. ShopRepository interface is created which will extends CrudRepository<ClassName, ID>

package com.example.thymeleaf.repository;

import java.util.List;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;

import com.example.thymeleaf.model.Shop;

@Repository
public interface ShopRepository extends JpaRepository<Shop, Integer>{

 //Custom query
 @Query(value = "select * from shop s where s.owner_name like %:keyword% or s.shop_type like %:keyword%", nativeQuery = true)
 List<Shop> findByKeyword(@Param("keyword") String keyword);
}

Here, we can see that the custom native query is created by using @Query to search for the owner_name field and shop_type filed.

@Param annotation is used to bind method parameters to a query.

Create a Service Layer

This layer is responsible to handle business logic. Here, we will create the ShopService.

package com.example.thymeleaf.service;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.example.thymeleaf.model.Shop;
import com.example.thymeleaf.repository.ShopRepository;
@Service
public class ShopService {
 @Autowired
 private ShopRepository repository;
 
 /*
  * TODO: Get the List of Shops
  */
 public List<Shop> getAllShops(){
  List<Shop> list =  (List<Shop>)repository.findAll();
  return list;
 }
 
 /*
  * TODO: Get Shop By keyword
  */
 public List<Shop> getByKeyword(String keyword){
  return repository.findByKeyword(keyword);
 }
}
  • First, inject the ShopRepository interface using Autowiring.
  • The method of the repository interface is called inside the getKeyword() method and getAllShops().

Create a Controller

The request for the web pages will be handle by the handler methods in the controller class using @GetMapping.

ShopController.java

package com.example.thymeleaf.controller;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;

import com.example.thymeleaf.model.Shop;
import com.example.thymeleaf.service.ShopService;
@Controller
public class ShopController {

 @Autowired
 private ShopService service;
 @RequestMapping(path = {"/","/search"})
 public String home(Shop shop, Model model, String keyword) {
  if(keyword!=null) {
   List<Shop> list = service.getByKeyword(keyword);
   model.addAttribute("list", list);
  }else {
  List<Shop> list = service.getAllShops();
  model.addAttribute("list", list);}
  return "index";
 }
}
  • @Controller annotation marks the ShopController class a Request Handler.
  • Every request coming for the ‘/’, ‘/search’ URL will be handled by the home() method. It would redirect you to the index page.
  • Here, if the keyword is null the getAllShops() method of the Service class will be invoked else getByKeyword() method would be invoked.

Create View using Thymeleaf

Go to src/main/resources/template folder and create an index.html file. Now inside the index.html file make sure to add the following code:

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

index.html

<!DOCTYPE html>
<html xmlns:th="www.thymeleaf.org">
<head>
<meta charset="ISO-8859-1">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.0/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-KyZXEAg3QhqLMpG8r+8fhAXLRk2vvoC2f3B09zVXn8CA5QIVfZOJ3BCsw2P0p/We" crossorigin="anonymous">
<script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.9.3/dist/umd/popper.min.js" integrity="sha384-eMNCOe7tC1doHpGoWe/6oMVemdAVTMs2xqW4mwXrXsW0L84Iytr2wi5v2QjrP/xp" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.0/dist/js/bootstrap.min.js" integrity="sha384-cn7l7gDp0eyniUwwAZgrzD06kc/tftFf19TOAs2zVinnD/C7E91j9yyk5//jjpt/" crossorigin="anonymous"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
<title>SEARCH</title>
</head>
<body>
<div class="container mt-5">
<h2 align="center">Search in Thymeleaf Template</h2>
  <form th:action="@{/search}" method="get">
  <input type="text" name="keyword" th:value="${keyword}"  placeholder="Find by Owner Name & Shop Type" size="50" >
  <button type="submit" class="btn btn-info" >Search</button>
  <button type="submit" class="btn btn-info" >Reset</button>
  </form>
  <table class="table table-bordered table-sm mt-2">
    <thead>
      <tr>
        <th>Id</th>
        <th>Owner Name</th>
        <th>Shop Type</th>
        <th>Shop Number</th>
        <th>Address</th>
        <th>Country</th>
        <th>Action</th>
      </tr>
    </thead>
    <tbody>
      <tr th:each="l : ${list}" th:field="${l}">
        <td th:text="${lStat.index+1}"></td>
        <td th:text="${l.ownerName}"></td>
        <td th:text="${l.shopType}"></td>
        <td th:text="${l.shopNo}"></td>
        <td th:text="${l.address}"></td>
        <td th:text="${l.country}"></td>
        <td>
        <a th:href="@{/update/{id}(id=${l.id})}" ><i class="fa fa-edit" ></i></a>
        <a th:href="@{/delete/{id}(id=${l.id})}" ><i class="fa fa-remove"></i></a>
        </td>
      </tr>
    </tbody>
  </table>
</div>
</body>
</html>

Here, the th: each attribute is used to iterate over the list of shops. The model attribute is accessed using ${} notation.

Here, the important code is to create a search text box field as shown below:

There is a complete article on how to iterate list  https://codebun.com/iterate-list-in-thymeleaf-using-spring-boot/

Run the Application

Now, Run the ThymeleafLesson8Application and Go to localhost:8888 and see the following output.

In this way, we have learned how to filter data using the thymeleaf template and Spring data JPA.