To use paging and sorting APIs provided by Spring Data JPA, your repository interface must extend the
PagingAndSortingRepository
interface.
Page<T> findAll(Pageable pageable)
– returns a page of entities meeting the paging
restriction provided in the Pageable
object.Iterable<T> 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);
For sorting, we are going to use the below method from the PagingAndSortingRepository
interface:
Iterable < T > findAll(Sort sort);
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 and sorting using Spring Data JPA.
Spring Boot provides a web tool called start.spring.io to bootstrap an application quickly. Just go to start.spring.io and generate a new spring boot project.
Use the below details in the Spring boot creation:
Project Name: spring-data-jpa-course
Project Type: Maven
Choose dependencies: Spring Data JPA, MySQL Driver, Lombok
Package name: net.javaguides.springboot
Here is the complete pom.xml
for your reference:
<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>
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 will 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
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;
import org.springframework.data.repository.query.Param;
import java.math.BigDecimal;
public interface ProductRepository extends JpaRepository
{
}
Let's write the JUnit test and within the JUnit test, we will write a logic to implement pagination and sorting 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 org.springframework.data.domain.Sort;
import java.util.List;
@SpringBootTest
public class PaginationAndSortingTest {
@Autowired
private ProductRepository productRepository;
@Test
void paginationAndSortingTogether(){
String sortBy = "price";
String sortDir = "desc";
int pageNo = 1;
int pageSize = 5;
// Sort object
Sort sort = sortDir.equalsIgnoreCase(Sort.Direction.ASC.name())?
Sort.by(sortBy).ascending(): Sort.by(sortBy).descending();
// Pageable object
Pageable pageable = PageRequest.of(pageNo, pageSize, sort);
Page<Product> page = productRepository.findAll(pageable);
List<Product> 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);
}
}
Note that Spring Data JPA behind scenes uses Hibernate to generate the below SQL query for pagination and sorting:
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_
order by
product0_.price desc limit ?,
?