Implement Pagination using Spring Boot 3, Spring Data JPA, and MySQL

Implement Pagination using Spring Boot 3, Spring Data JPA, and MySQL

Overview

In this tutorial, We are going to implement pagination using Spring Boot 3, Spring Data JPA and MySQL. We will keep it simple and easy to understand!

Dealing with large amounts of data in modern applications is a common challenge. Showing all records at once can slow things down and make your application feel clunky. That’s where pagination comes in. Pagination lets you break down a big set of data into smaller, manageable chunks (pages), making your application faster and easier for users to navigate.

Pagination using spring boot, jpa and mysql

Pagination is one of the most essential techniques in modern backend development to handle large datasets efficiently. In this complete tutorial, you’ll learn how to implement pagination in Spring Boot using:

  • Spring Data JPA’s built-in Pageable
  • Custom pagination queries using native SQL
  • Custom DTO responses for frontend control
  • Real-time API testing with Postman

What is Pagination?

Pagination helps break down large results into smaller manageable chunks (pages). For example, instead of retrieving 10,000 records, you can fetch 10 records per request with page=0&size=10. This is crucial for performance, user experience, and frontend rendering.

The main details you need for pagination are:

  • Page Number: Which specific page you want to see (this usually starts from 0 or 1, depending on how you set it up).
  • Page Size: How many records you want to display on each page.

⚙️ Technologies Used

  • Java
  • Spring Boot
  • Spring Data JPA
  • MySQL
  • Postman

Now let’s create the spring boot application and implement pagination api step-by-step

Step 1: Let’s Get Started! (Project Setup)

First, let’s set up a basic Spring Boot project. You can use Spring Initializr (https://start.spring.io/) and add the following dependencies:

  • Spring Web: For building RESTful APIs.
  • Spring Data JPA: Through this spring data jpa we will interact with database
  • MySQL Driver: To connect to your MySQL database.
  • Lombok (Optional but Recommended): Reduces boilerplate code like getters and setters.

Step 2 : Configure the application.properties File

1. Open the application.properties file and add all the information of your database. Here’s an example configuration:


# Server configuration
server.port = 8081
          
# Database configuration
spring.datasource.url=jdbc:mysql://localhost:3306/your_database_name
spring.datasource.name=your_database_name
spring.datasource.username=your_username
spring.datasource.password=your_password
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
          
# JPA configuration
spring.jpa.hibernate.ddl-auto=update
        

For More Details:

If you want to learn more about configuring the application.properties file, check out this detailed article:

Create REST APIs Using Spring Boot: Step-by-Step Guide

Step 3 : Entity Class – Product.java


@Entity
public class Product {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    private String name;
    private double price;

    // Getters and Setters
}

This class defines the product table with id, name, and price.

Step 4 : Repository Layer – ProductRepository


@Repository
public interface ProductRepository extends JpaRepository<Product, Long>, PagingAndSortingRepository<Product, Long> {

    // Custom Pageable Query
    @Query(value = "SELECT * FROM product WHERE id < 14", 
           countQuery = "SELECT COUNT(*) FROM product WHERE id < 14", 
           nativeQuery = true)
    Page<Product> getAllProductsPage(Pageable pageable);

    // Native SQL with LIMIT OFFSET
    @Query(value = "SELECT * FROM product LIMIT :limit OFFSET :offSet", nativeQuery = true)
    List<Product> getProdustWithCustomQuery(@Param("limit") int limit, 
                                              @Param("offSet") int offSet);

    // Get total count of all products
    @Query(value = "SELECT COUNT(*) FROM product", nativeQuery = true)
    Long getTotalCount();
}

🔍 Explanation:

  • JpaRepository and PagingAndSortingRepository give default pagination capabilities.
  • getAllProductsPage() fetches data with pagination using a native SQL query and returns a Page<Product>.
  • getProdustWithCustomQuery() manually handles pagination with LIMIT and OFFSET.
  • getTotalCount() gives total number of records for custom pagination response.

Step 5 : Service Layer – ProductService


@Service
public class ProductService {

    @Autowired
    private ProductRepository productRepository;

    // Default pagination using findAll(Pageable)
    public Page<Product> getAllProducts(int pageSize, int pageNumber) {
        Pageable pageable = PageRequest.of(pageNumber, pageSize);
        return productRepository.findAll(pageable);
    }

    // Pagination with custom WHERE clause
    public Page<Product> getAllProductWithPage(int pageSize, int pageNumber) {
        Pageable pageable = PageRequest.of(pageNumber, pageSize);
        return productRepository.getAllProductsPage(pageable);
    }

    // Custom paginated response
    public ProductResponse getProductWithCustomQuery(int pageSize, int pageNumber) {
        int offSet = pageNumber * pageSize;
        List<Product> products = productRepository.getProdustWithCustomQuery(pageSize, offSet);
        Long totalCount = productRepository.getTotalCount();
        return new ProductResponse(products, totalCount);
    }
}

🔍 Explanation:

  • PageRequest.of(page, size) creates a Pageable object.
  • Each method uses a different approach:
    • Default JPA pagination.
    • Custom pagination with a native query.
    • Fully custom pagination with total count in a separate DTO.

Step 6 : DTO Class – ProductResponse.java


public class ProductResponse {

    private List<Product> products;
    private Long totalCount;

    public ProductResponse(List<Product> products, Long totalCount) {
        this.products = products;
        this.totalCount = totalCount;
    }

    // Getters and Setters

    public List<Product> getProducts() {
        return products;
    }

    public void setProducts(List<Product> products) {
        this.products = products;
    }

    public Long getTotalCount() {
        return totalCount;
    }

    public void setTotalCount(Long totalCount) {
        this.totalCount = totalCount;
    }
}

This class wraps your product data and total record count for frontend-friendly pagination (e.g., React, Angular, etc.).

Step 7 : Controller Layer – ProductController


@RestController
public class ProductController {

    @Autowired
    private ProductService productService;

    // Default JPA Pagination
    @CrossOrigin(origins = "http://localhost:3000/")
    @GetMapping("/getProducts")
    public ResponseEntity<Page<Product>> getAllProducts(
        @RequestParam(defaultValue = "0") int page,
        @RequestParam(defaultValue = "10") int size) {
        return ResponseEntity.ok(productService.getAllProducts(size, page));
    }

    // Pagination using custom WHERE clause
    @GetMapping("/getProductsWithPage")
    public ResponseEntity<Page<Product>> getAllProductsWithPage(
        @RequestParam(defaultValue = "0") int page,
        @RequestParam(defaultValue = "10") int size) {
        return ResponseEntity.ok(productService.getAllProductWithPage(size, page));
    }

    // Fully custom pagination
    @GetMapping("/getProductWithCustomQuery")
    public ResponseEntity<ProductResponse> getProductsWithCustomQuery(
        @RequestParam(defaultValue = "0") int page,
        @RequestParam(defaultValue = "10") int size) {
        return ResponseEntity.ok(productService.getProductWithCustomQuery(size, page));
    }
}

🔍 Explanation:

  • All APIs are GET methods.
  • Pagination parameters page and size are passed via query params.
  • You return different types of responses:
    • Page<Product> – standard paginated response.
    • ProductResponse – custom response with control over structure.

🧪 Testing APIs with Postman

Use the following URLs to test your pagination logic:

URL Description
http://localhost:8080/getProducts?page=0&size=5 JPA default pagination
http://localhost:8080/getProductsWithPage?page=1&size=5 Custom query with Pageable
http://localhost:8080/getProductWithCustomQuery?page=2&size=3 Custom response with native query

Conclusion

In this tutorial, you learned how to implement pagination in Spring Boot using different techniques:

  • ✅ JPA’s built-in pagination with Pageable
  • ✅ Custom queries using @Query
  • ✅ Manual pagination with native SQL
  • ✅ Custom DTO responses for frontend consumption

This approach helps improve performance, frontend compatibility, and scalability in your Spring Boot applications.

These techniques not only help in improving performance but also allow you to handle large datasets with ease, especially when integrating with frontend frameworks like React or Angular.
Whether you’re building admin panels, product listings, or report tables, proper pagination is a must-have feature. By applying the techniques shared in this guide, you’ll be able to build scalable, maintainable, and production-ready pagination logic for any Java Spring Boot project.

Youtube Video –

Explore our more articles

What is pagination in Spring Boot?

Pagination is one of the most essential techniques in modern backend development to handle large datasets efficiently. In this complete tutorial, you’ll learn how to implement pagination in Spring Boot using:

Why is pagination important in APIs?

Pagination improves performance and user experience by:
– Reducing the size of each API response
– Loading data faster on the frontend
– Preventing memory and bandwidth issues

Implement Pagination in Spring Boot step-by-step ?

⚙️ Technologies Used
Java
Spring Boot
Spring Data JPA
MySQL
Postman

Now let’s create the spring boot application and implement pagination api step-by-step

1 thought on “Implement Pagination using Spring Boot 3, Spring Data JPA, and MySQL”

Leave a Comment