Search Tutorials


Spring Boot + Elasticsearch + Pagination Example | JavaInUse

Spring Boot + Elasticsearch + Pagination Example

In previous tutorial we will implemented Spring Boot + Elasticsearch CRUD example. In this tutorial we will be implementing pagination in a Spring Boot application with Elasticsearch as the data store.

Pagination

Displaying large amounts of data in a single view can be overwhelming and hinder user experience. A common approach to address this issue is pagination, where the data is divided into smaller, manageable batches or pages. By implementing pagination, you can improve both the user interface (UI) and the overall performance of your application.

Spring Boot 3 + Pagination Example
Suppose we have 100 records that need to be displayed. Instead of rendering all 100 records at once, which could result in a cluttered UI and potentially slow performance, we can divide the records into batches or pages. In our example, we have chosen a batch size of 10 records. Initially, only the first 10 records are displayed on the page. This provides a cleaner and more focused view for the user. Additionally, by only fetching and rendering 10 records at a time, we reduce the amount of data that needs to be transferred over the network and processed by the client, thereby improving the overall performance and responsiveness of your application. To navigate through the remaining records, you can include pagination controls, such as "Next" and "Previous" buttons or page numbers.

Video

This tutorial is explained in the below Youtube Video.

Implementation

For our current implementation we have 10 employee records in elasticsearch. If suppose we keep the page size as 3, then we will be having 4 pages as follows-
Spring Boot 3 + Pagination Example
The spring boot project we will be implementing is as follows -
Spring Boot 3 + Elasticsearch CRUD + Pagination Example
The EmployeeRepository we had implemented previously, it provided CRUD operations by default. For this EmployeeRepository we will be adding a new method named findAll to which we pass a Pageable instance. When called with a Pageable instance, Spring Data Elasticsearch will automatically construct the appropriate Elasticsearch query, execute it against the index, and return a Page<Employee> object containing the paginated result set.
package com.javainuse.boot_elasticsearch_crud.repository;

import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;

import com.javainuse.boot_elasticsearch_crud.model.Employee;

public interface EmployeeRepository extends ElasticsearchRepository<Employee, String> {

	Page<Employee> findAll(Pageable pageable);

}
In the EmployeeService class add a new method getPaginatedEmployees which takes 2 parameters page and size. It returns paginated result set containing EmployeeDto objects.
package com.javainuse.boot_elasticsearch_crud.service;

import java.util.List;

import org.springframework.data.domain.Page;

import com.javainuse.boot_elasticsearch_crud.exception.EmployeeNotFoundException;
import com.javainuse.boot_elasticsearch_crud.model.EmployeeDto;

public interface EmployeeService {
	EmployeeDto createEmployee(EmployeeDto employeeDto);
	EmployeeDto getEmployeeById(String employeeId) throws EmployeeNotFoundException;
	List<EmployeeDto> getEmployees();
	void deleteEmployee(String employeeId) throws EmployeeNotFoundException;
	EmployeeDto updateEmployee(EmployeeDto employeeDto) throws EmployeeNotFoundException;
	Page<EmployeeDto> getPaginatedEmployees(int page, int size);
}




In the EmployeeServiceImpl provide the implementation of the above getPaginatedEmployees method.
It retrieves a paginated result set of Employee entities from the underlying data store i.e. Elasticsearch using the Spring Data repository.
It then maps each Employee entity to an EmployeeDto object using the EmployeeMapper utility class. Finally, it returns a Page<EmployeeDto> object containing the paginated result set of EmployeeDto objects, preserving the pagination metadata.
package com.javainuse.boot_elasticsearch_crud.service;

import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.stereotype.Service;

import com.javainuse.boot_elasticsearch_crud.exception.EmployeeNotFoundException;
import com.javainuse.boot_elasticsearch_crud.mapper.EmployeeMapper;
import com.javainuse.boot_elasticsearch_crud.model.Employee;
import com.javainuse.boot_elasticsearch_crud.model.EmployeeDto;
import com.javainuse.boot_elasticsearch_crud.repository.EmployeeRepository;


@Service
public class EmployeeServiceImpl implements EmployeeService {

	@Autowired
	private EmployeeRepository employeeRepository;

	@Override
	public EmployeeDto createEmployee(EmployeeDto employeeDto) {
		Employee employee = EmployeeMapper.mapToEmployee(employeeDto);
		Employee createdEmployee = employeeRepository.save(employee);
		return EmployeeMapper.mapToEmployeeDto(createdEmployee);
	}

	@Override
	public EmployeeDto getEmployeeById(String employeeId) throws EmployeeNotFoundException {
		Optional<Employee> employee = employeeRepository.findById(employeeId);
		if (employee.isEmpty()) {
			throw new EmployeeNotFoundException("Employee with id - " + employeeId + " not found.");
		}
		return EmployeeMapper.mapToEmployeeDto(employee.get());
	}

	@Override
	public List<EmployeeDto> getEmployees() {
		Iterable<Employee> employeesIterable = employeeRepository.findAll();
		List<Employee> employees = StreamSupport.stream(employeesIterable.spliterator(), false).toList();
		return employees.stream().map((emp) -> EmployeeMapper.mapToEmployeeDto(emp)).collect(Collectors.toList());
	}

	@Override
	public void deleteEmployee(String employeeId) throws EmployeeNotFoundException {
		Optional<Employee> employee = employeeRepository.findById(employeeId);
		if (employee.isEmpty()) {
			throw new EmployeeNotFoundException("Employee with id - " + employeeId + " not found.");
		}
		employeeRepository.deleteById(employeeId);
	}

	@Override
	public EmployeeDto updateEmployee(EmployeeDto employeeDto) throws EmployeeNotFoundException {
		Optional<Employee> retrievedEmployee = employeeRepository.findById(employeeDto.getId());
		if (retrievedEmployee.isEmpty()) {
			throw new EmployeeNotFoundException("Employee with id - " + employeeDto.getId() + " not found.");
		}
		Employee employee = retrievedEmployee.get();
		employee.setName(employeeDto.getName());
		employee.setDepartment(employeeDto.getDepartment());
		Employee createdEmployee = employeeRepository.save(employee);
		return EmployeeMapper.mapToEmployeeDto(createdEmployee);
	}

	@Override
	public Page<EmployeeDto> getPaginatedEmployees(int pageNo, int size) {
		PageRequest pageable = PageRequest.of(pageNo, size);
		Page<Employee> employeePage = employeeRepository.findAll(pageable);
		Page<EmployeeDto> employeeDtoPage = employeePage.map(EmployeeMapper::mapToEmployeeDto);
		return employeeDtoPage;
	}
}
Finally in the EmployeeController define a RESTful web service endpoint /employeepage that accepts page and size parameters as query parameters or form data. It calls the getPaginatedEmployees method of the employeeService to retrieve a Page<EmployeeDto> object containing the paginated result set of EmployeeDto objects. Finally, it returns a ResponseEntity with the paginated result set as the response body and the HTTP status code 200 OK.
package com.javainuse.boot_elasticsearch_crud.controller;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import com.javainuse.boot_elasticsearch_crud.exception.EmployeeNotFoundException;
import com.javainuse.boot_elasticsearch_crud.model.EmployeeDto;
import com.javainuse.boot_elasticsearch_crud.service.EmployeeService;

@RestController
public class EmployeeController {

	@Autowired
	private EmployeeService employeeService;

	@PostMapping(value = "/employee")
	public ResponseEntity<EmployeeDto> createEmployee(@RequestBody EmployeeDto employeeDto) {
		EmployeeDto createdEmployee = employeeService.createEmployee(employeeDto);
		return new ResponseEntity<>(createdEmployee, HttpStatus.CREATED);
	}

	@GetMapping(value = "/employee/{employeeId}")
	public ResponseEntity<EmployeeDto> getEmployeeById(@PathVariable("employeeId") String employeeId)
			throws EmployeeNotFoundException {
		try {
			EmployeeDto employee = employeeService.getEmployeeById(employeeId);
			return new ResponseEntity<>(employee, HttpStatus.OK);
		} catch (EmployeeNotFoundException employeeNotFoundException) {
			throw employeeNotFoundException;
		}
	}

	@GetMapping(value = "/employees")
	public ResponseEntity<List<EmployeeDto>> getEmployees() {
		List<EmployeeDto> employees = employeeService.getEmployees();
		return new ResponseEntity<>(employees, HttpStatus.OK);
	}

	@DeleteMapping(value = "/employee/{employeeId}")
	public ResponseEntity<HttpStatus> deleteEmployees(@PathVariable("employeeId") String employeeId)
			throws EmployeeNotFoundException {
		employeeService.deleteEmployee(employeeId);
		return new ResponseEntity<>(HttpStatus.NO_CONTENT);
	}

	@PutMapping(value = "/employee")
	public ResponseEntity<EmployeeDto> updateEmployee(@RequestBody EmployeeDto employeeDto)
			throws EmployeeNotFoundException {
		EmployeeDto createdEmployee = employeeService.updateEmployee(employeeDto);
		return new ResponseEntity<>(createdEmployee, HttpStatus.OK);
	}

	@GetMapping(value = "/employeepage")
	public ResponseEntity<Page<EmployeeDto>> getPaginatedEmployees(@RequestParam int pageNo, @RequestParam int size) {
		Page<EmployeeDto> pageEmployee = employeeService.getPaginatedEmployees(pageNo, size);
		return new ResponseEntity<>(pageEmployee, HttpStatus.OK);
	}
}
Open the command prompt as an admin. Go to the elasticsearch bin folder and type the following command
elasticsearch.bat
This will start elasticsearch.
elasticsearch start
If we now go to http://localhost:8080/employeepage?page=1&size=2
Spring Boot 3 + Elasticsearch CRUD + Pagination Output

Download Source Code

Download it -
Spring Boot 3 + Elasticsearch + Pagination Example