In this tutorial, we will learn how to implement Pagination using Spring Data JPA.
As you know, pagination allows the users to see a small portion of data at a time.
Pagination consists of two fields – page size and page number - These two fields we will use while implementing pagination using Spring Data JPA.
To use paging and sorting APIs provided by Spring Data JPA, your repository interface must extend the
PagingAndSortingRepository
interface.
PagingAndSortingRepository
is an extension of the CrudRepository
to provide additional methods to retrieve
entities using the pagination and sorting abstraction. It provides two methods :
Page findAll(Pageable pageable)
– returns a Page of entities meeting
the paging restriction provided in the Pageable object.
Iterable findAll(Sort sort)
– returns all entities sorted by the given
options. No paging is applied here.Here is the internal source code of the PagingAndSortingRepository
interface:
@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);
}
JpaRepository
interface extends the PagingAndSortingRepository
interface so if your repository interface is of
type JpaRepository
, you don’t have to make a change to it.
For pagination, we are going to use the below method from the PagingAndSortingRepository
interface:
Page < T > findAll(Pageable pageable);
Note: Spring Data JPA has SimpleJPARepository
class which implements
PagingAndSortingRepository
interface
methods so we don't have to write a code to implement PagingAndSortingRepository
interface methods.
Let's create a Spring boot project from the scratch and let's implement pagination using Spring Data JPA.
Spring Boot provides a web tool called https://start.spring.io to bootstrap an application quickly. Just go to https://start.spring.io and generate a new spring boot project.
Here is the complete pom.xml
for your reference:
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.0.4</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>net.javaguides</groupId>
<artifactId>spring-data-jpa-course</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring-data-jpa-course</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</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>
Let's use the MySQL database to store and retrieve the data in this example and we gonna use Hibernate properties to create and drop tables.
Open the application.properties
file and add the following configuration to
it:
spring.datasource.url=jdbc:mysql://localhost:3306/demo?useSSL=false
spring.datasource.username=root
spring.datasource.password=Mysql@123
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQLDialect
spring.jpa.hibernate.ddl-auto = create-drop
Make sure that you create a demo database before running the Spring Boot application. Also, change the MySQL username and password as per your MySQL installation on your machine.
Let's create an entity
package inside a base package
"net.javaguides.springboot".
Within the entity
package, create a Product
class with the following content:
import lombok.*;
import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.UpdateTimestamp;
import jakarta.persistence.*;
import java.math.BigDecimal;
import java.time.LocalDateTime;
@Entity
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@ToString
@Table(
name = "products",
schema = "ecommerce",
uniqueConstraints = {
@UniqueConstraint(
name = "sku_unique",
columnNames = "stock_keeping_unit"
)
}
)
public class Product {
@Id
@GeneratedValue(
strategy = GenerationType.SEQUENCE,
generator = "product_generator"
)
@SequenceGenerator(
name = "product_generator",
sequenceName = "product_sequence_name",
allocationSize = 1
)
private Long id;
@Column(name = "stock_keeping_unit", nullable = false)
private String sku;
@Column(nullable = false)
private String name;
private String description;
private BigDecimal price;
private boolean active;
private String imageUrl;
@CreationTimestamp
private LocalDateTime dateCreated;
@UpdateTimestamp
private LocalDateTime lastUpdated;
}
Note that we are using Lombok annotations to reduce the boilerplate code.
The next thing we’re gonna do is to create a repository to access Product
entity data from the database.
The JpaRepository
interface defines methods for all the CRUD operations on
the entity, and a default
implementation of the JpaRepository
called SimpleJpaRepository
.
Let's create a repository
package inside a base package
"net.javaguides.springdatarest".
Within the repository
package, create a ProductRepository
interface with the following content:
import com.springdatajpa.springboot.entity.Product;
import org.springframework.data.jpa.repository.JpaRepository;
public interface ProductRepository extends JpaRepository {
}
Let's write the JUnit test and within the JUnit test, we will write a logic to implement pagination using Spring Data JPA:
import com.springdatajpa.springboot.entity.Product;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import java.util.List;
@SpringBootTest
public class PaginationAndSortingTest {
@Autowired
private ProductRepository productRepository;
@Test
void pagination(){
int pageNo = 0;
int pageSize = 5;
// create pageable object
Pageable pageable = PageRequest.of(pageNo, pageSize);
// findAll method and pass pageable instance
Page page = productRepository.findAll(pageable);
List products = page.getContent();
products.forEach((p) ->{
System.out.println(p);
});
// total pages
int totalPage = page.getTotalPages();
// total elements
long totalElements = page.getTotalElements();
// number of elements
int numberOfElements = page.getNumberOfElements();
// size
int size = page.getSize();
// last
boolean isLast = page.isLast();
// first
boolean isFirst = page.isFirst();
System.out.println("total page -> " + totalPage);
System.out.println("totalElements -> " + totalElements);
System.out.println("numberOfElements -> " + numberOfElements);
System.out.println(" size ->" + size);
System.out.println(" isLast -> " + isLast);
System.out.println(" isFirst -> " + isFirst);
}
}
Spring Data JPA uses 0 to retrieve first-page information from the database.
To apply only pagination in the result set, we need to create a Pageable
object without any Sort information
and pass Pageable
object to findAll()
method:
// create pageable object
Pageable pageable = PageRequest.of(pageNo, pageSize);
// findAll method and pass pageable instance
Page<Product> page = productRepository.findAll(pageable);
We will retrieve actual content from Page using the getContent() method:
List<Product> products = page.getContent();
We use Page class API's to get more information about pagination:
// total pages
int totalPage = page.getTotalPages();
// total elements
long totalElements = page.getTotalElements();
// number of elements
int numberOfElements = page.getNumberOfElements();
// size
int size = page.getSize();
// last
boolean isLast = page.isLast();
// first
boolean isFirst = page.isFirst();
System.out.println("total page -> " + totalPage);
System.out.println("totalElements -> " + totalElements);
System.out.println("numberOfElements -> " + numberOfElements);
System.out.println(" size ->" + size);
System.out.println(" isLast -> " + isLast);
System.out.println(" isFirst -> " + isFirst);
Once you run the JUnit test, you will get the below output:
Note that Hibernate generated SQL query for pagination:
select
product0_.id as id1_0_,
product0_.active as active2_0_,
product0_.date_created as date_cre3_0_,
product0_.description as descript4_0_,
product0_.image_url as image_ur5_0_,
product0_.last_updated as last_upd6_0_,
product0_.name as name7_0_,
product0_.price as price8_0_,
product0_.stock_keeping_unit as stock_ke9_0_
from
products product0_ limit ?
Hibernate:
select
count(product0_.id) as col_0_0_
from
products product0_