🎓 Top 15 Udemy Courses (80-90% Discount): My Udemy Courses - Ramesh Fadatare — All my Udemy courses are real-time and project oriented courses.
▶️ Subscribe to My YouTube Channel (178K+ subscribers): Java Guides on YouTube
▶️ For AI, ChatGPT, Web, Tech, and Generative AI, subscribe to another channel: Ramesh Fadatare on YouTube
In this PART 7, we will implement Sorting for our employee list page using Spring Data JPA and Thymeleaf.
Let's understand more in the example section.
		← Previous 
 Next 
	→ 
Use the below links to navigate different parts of this tutorial:
1. Spring Boot Thymeleaf CRUD Database Real-Time Project - PART 1- Create and Setup Spring Boot Project in Eclipse STS
- Database Setup
Understand Spring Data JPA’s Sorting API
The Spring Data JPA provides PagingAndSortingRepository interface which supports spring and pagination with the following APIs:
@NoRepositoryBean
public interface PagingAndSortingRepository < T, ID > extends CrudRepository < T, ID > {
    /**
     * Returns all entities sorted by the given options.
     *
     * @param sort
     * @return all entities sorted by the given options
     */
    Iterable < T > findAll(Sort sort);
    /**
     * Returns a {@link Page} of entities meeting the paging restriction provided in the {@code Pageable} object.
     *
     * @param pageable
     * @return a page of entities
     */
    Page < T > findAll(Pageable pageable);
}
The users will be able to sort the employee's list by clicking on the column header of the table. 
First, create a Sort object like this:
Sort sort = Sort.by(“fieldName”).ascending();
This will sort the result by fieldName in ascending order. fieldName must match a field name declared in the entity class. 
We can also sort by more than one field, for example:
Sort sort = Sort.by("fieldName1").ascending().and(Sort.by("fieldName2").descending());
Then we pass the Sort object to create a Pageable as follows:
Pageable pageable = PageRequest.of(pageNum - 1, pageSize, sort);
1. Back-end changes
1. EmployeeService.java interface changes
Let's add two fields to the existing method:
Page<Employee> findPaginated(int pageNo, int pageSize, String sortField, String sortDirection);
The complete code:
package net.javaguides.springboot.service;
import java.util.List;
import org.springframework.data.domain.Page;
import net.javaguides.springboot.model.Employee;
public interface EmployeeService {
    List < Employee > getAllEmployees();
    void saveEmployee(Employee employee);
    Employee getEmployeeById(long id);
    void deleteEmployeeById(long id);
    Page < Employee > findPaginated(int pageNo, int pageSize, String sortField, String sortDirection);
}
2. EmployeeServiceImpl.java class changes
The sorting logic implemented in the below method:
@Override
public Page<Employee> findPaginated(int pageNo, int pageSize, String sortField, String sortDirection) {
 Sort sort = sortDirection.equalsIgnoreCase(Sort.Direction.ASC.name()) ? Sort.by(sortField).ascending() :
  Sort.by(sortField).descending();
 
 Pageable pageable = PageRequest.of(pageNo - 1, pageSize, sort);
 return this.employeeRepository.findAll(pageable);
}
The complete code:
package net.javaguides.springboot.service;
import java.util.List;
import java.util.Optional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import net.javaguides.springboot.model.Employee;
import net.javaguides.springboot.repository.EmployeeRepository;
@Service
public class EmployeeServiceImpl implements EmployeeService {
    @Autowired
    private EmployeeRepository employeeRepository;
    @Override
    public List < Employee > getAllEmployees() {
        return employeeRepository.findAll();
    }
    @Override
    public void saveEmployee(Employee employee) {
        this.employeeRepository.save(employee);
    }
    @Override
    public Employee getEmployeeById(long id) {
        Optional < Employee > optional = employeeRepository.findById(id);
        Employee employee = null;
        if (optional.isPresent()) {
            employee = optional.get();
        } else {
            throw new RuntimeException(" Employee not found for id :: " + id);
        }
        return employee;
    }
    @Override
    public void deleteEmployeeById(long id) {
        this.employeeRepository.deleteById(id);
    }
    @Override
    public Page < Employee > findPaginated(int pageNo, int pageSize, String sortField, String sortDirection) {
        Sort sort = sortDirection.equalsIgnoreCase(Sort.Direction.ASC.name()) ? Sort.by(sortField).ascending() :
            Sort.by(sortField).descending();
        Pageable pageable = PageRequest.of(pageNo - 1, pageSize, sort);
        return this.employeeRepository.findAll(pageable);
    }
}
3.EmployeeController.java changes
Let's change the existing method to provide a support for sorting:
@GetMapping("/page/{pageNo}")
public String findPaginated(@PathVariable(value = "pageNo") int pageNo,
    @RequestParam("sortField") String sortField,
    @RequestParam("sortDir") String sortDir,
    Model model) {
    int pageSize = 5;
    Page < Employee > page = employeeService.findPaginated(pageNo, pageSize, sortField, sortDir);
    List < Employee > listEmployees = page.getContent();
    model.addAttribute("currentPage", pageNo);
    model.addAttribute("totalPages", page.getTotalPages());
    model.addAttribute("totalItems", page.getTotalElements());
    model.addAttribute("sortField", sortField);
    model.addAttribute("sortDir", sortDir);
    model.addAttribute("reverseSortDir", sortDir.equals("asc") ? "desc" : "asc");
    model.addAttribute("listEmployees", listEmployees);
    return "index";
}
Also provide default sorting field and sorting direction for home page:
    // display list of employees
    @GetMapping("/")
    public String viewHomePage(Model model) {
        return findPaginated(1, "firstName", "asc", model);
    }
The complete code:
package net.javaguides.springboot.controller;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import net.javaguides.springboot.model.Employee;
import net.javaguides.springboot.service.EmployeeService;
@Controller
public class EmployeeController {
    @Autowired
    private EmployeeService employeeService;
    // display list of employees
    @GetMapping("/")
    public String viewHomePage(Model model) {
        return findPaginated(1, "firstName", "asc", model);
    }
    @GetMapping("/showNewEmployeeForm")
    public String showNewEmployeeForm(Model model) {
        // create model attribute to bind form data
        Employee employee = new Employee();
        model.addAttribute("employee", employee);
        return "new_employee";
    }
    @PostMapping("/saveEmployee")
    public String saveEmployee(@ModelAttribute("employee") Employee employee) {
        // save employee to database
        employeeService.saveEmployee(employee);
        return "redirect:/";
    }
    @GetMapping("/showFormForUpdate/{id}")
    public String showFormForUpdate(@PathVariable(value = "id") long id, Model model) {
        // get employee from the service
        Employee employee = employeeService.getEmployeeById(id);
        // set employee as a model attribute to pre-populate the form
        model.addAttribute("employee", employee);
        return "update_employee";
    }
    @GetMapping("/deleteEmployee/{id}")
    public String deleteEmployee(@PathVariable(value = "id") long id) {
        // call delete employee method 
        this.employeeService.deleteEmployeeById(id);
        return "redirect:/";
    }
    @GetMapping("/page/{pageNo}")
    public String findPaginated(@PathVariable(value = "pageNo") int pageNo,
        @RequestParam("sortField") String sortField,
        @RequestParam("sortDir") String sortDir,
        Model model) {
        int pageSize = 5;
        Page < Employee > page = employeeService.findPaginated(pageNo, pageSize, sortField, sortDir);
        List < Employee > listEmployees = page.getContent();
        model.addAttribute("currentPage", pageNo);
        model.addAttribute("totalPages", page.getTotalPages());
        model.addAttribute("totalItems", page.getTotalElements());
        model.addAttribute("sortField", sortField);
        model.addAttribute("sortDir", sortDir);
        model.addAttribute("reverseSortDir", sortDir.equals("asc") ? "desc" : "asc");
        model.addAttribute("listEmployees", listEmployees);
        return "index";
    }
}
2. Front-end changes
1.index.html
We make the header columns of the table sortable by adding hyperlinks with the following code:
<th>
      <a th:href="@{'/page/' + ${currentPage} + '?sortField=firstName&sortDir=' + ${reverseSortDir}}">
       Employee First Name</a>
     </th>
     <th>
     <a th:href="@{'/page/' + ${currentPage} + '?sortField=lastName&sortDir=' + ${reverseSortDir}}">
       Employee Last Name</a>
     </th>
     <th>
     <a th:href="@{'/page/' + ${currentPage} + '?sortField=email&sortDir=' + ${reverseSortDir}}">
       Employee Email</a>
     </th>
     <th> Actions </th>
We also need to change pagination part to provide sorting support like:
<div th:if = "${totalPages > 1}">
   <div class = "row col-sm-10">
    <div class = "col-sm-2">
     Total Rows: [[${totalItems}]] 
    </div>
    <div class = "col-sm-1">
     <span th:each="i: ${#numbers.sequence(1, totalPages)}">
      <a th:if="${currentPage != i}" th:href="@{'/page/' + ${i}+ '?sortField=' + ${sortField} + '&sortDir=' + ${sortDir}}">[[${i}]]</a>
      <span th:unless="${currentPage != i}">[[${i}]]</span>     
     </span>
    </div>
    <div class = "col-sm-1">
     <a th:if="${currentPage < totalPages}" th:href="@{'/page/' + ${currentPage + 1}+ '?sortField=' + ${sortField} + '&sortDir=' + ${sortDir}}">Next</a>
     <span th:unless="${currentPage < totalPages}">Next</span>
    </div>
    
     <div class="col-sm-1">
        <a th:if="${currentPage < totalPages}" th:href="@{'/page/' + ${totalPages}+ '?sortField=' + ${sortField} + '&sortDir=' + ${sortDir}}">Last</a>
     <span th:unless="${currentPage < totalPages}">Last</span>
        </div>
   </div>
  </div>
The complete code:
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="ISO-8859-1">
<title>Employee Management System</title>
<link rel="stylesheet"
 href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css"
 integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO"
 crossorigin="anonymous">
 
</head>
<body>
 <div class="container my-2">
 <h1>Employees List</h1>
 
 <a th:href = "@{/showNewEmployeeForm}" class="btn btn-primary btn-sm mb-3"> Add Employee </a>
 
  <table border="1" class = "table table-striped table-responsive-md">
   <thead>
    <tr>
     <th>
      <a th:href="@{'/page/' + ${currentPage} + '?sortField=firstName&sortDir=' + ${reverseSortDir}}">
       Employee First Name</a>
     </th>
     <th>
     <a th:href="@{'/page/' + ${currentPage} + '?sortField=lastName&sortDir=' + ${reverseSortDir}}">
       Employee Last Name</a>
     </th>
     <th>
     <a th:href="@{'/page/' + ${currentPage} + '?sortField=email&sortDir=' + ${reverseSortDir}}">
       Employee Email</a>
     </th>
     <th> Actions </th>
    </tr>
   </thead>
   <tbody>
    <tr th:each="employee : ${listEmployees}">
     <td th:text="${employee.firstName}"></td>
     <td th:text="${employee.lastName}"></td>
     <td th:text="${employee.email}"></td>
     <td> <a th:href="@{/showFormForUpdate/{id}(id=${employee.id})}" class="btn btn-primary">Update</a>
         <a th:href="@{/deleteEmployee/{id}(id=${employee.id})}" class="btn btn-danger">Delete</a>
     </td>
    </tr>
   </tbody>
  </table>
  
  <div th:if = "${totalPages > 1}">
   <div class = "row col-sm-10">
    <div class = "col-sm-2">
     Total Rows: [[${totalItems}]] 
    </div>
    <div class = "col-sm-1">
     <span th:each="i: ${#numbers.sequence(1, totalPages)}">
      <a th:if="${currentPage != i}" th:href="@{'/page/' + ${i}+ '?sortField=' + ${sortField} + '&sortDir=' + ${sortDir}}">[[${i}]]</a>
      <span th:unless="${currentPage != i}">[[${i}]]</span>     
     </span>
    </div>
    <div class = "col-sm-1">
     <a th:if="${currentPage < totalPages}" th:href="@{'/page/' + ${currentPage + 1}+ '?sortField=' + ${sortField} + '&sortDir=' + ${sortDir}}">Next</a>
     <span th:unless="${currentPage < totalPages}">Next</span>
    </div>
    
     <div class="col-sm-1">
        <a th:if="${currentPage < totalPages}" th:href="@{'/page/' + ${totalPages}+ '?sortField=' + ${sortField} + '&sortDir=' + ${sortDir}}">Last</a>
     <span th:unless="${currentPage < totalPages}">Last</span>
        </div>
   </div>
  </div>
 </div>
</body>
</html>
 
 
 
![[NEW] Full-Stack Java Development with Spring Boot 3 & React Build 5 Spring Boot Projects with Java: Line-by-Line Coding](https://img-c.udemycdn.com/course/750x422/5338984_4d3a_5.jpg) 
 
 
 
 
 
 
 
 
 
 
Comments
Post a Comment
Leave Comment