Spring Data JPA Specification With Pagination

📘 Premium Read: Access my best content on Medium member-only articles — deep dives into Java, Spring Boot, Microservices, backend architecture, interview preparation, career advice, and industry-standard best practices.

🎓 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 (176K+ subscribers): Java Guides on YouTube

▶️ For AI, ChatGPT, Web, Tech, and Generative AI, subscribe to another channel: Ramesh Fadatare on YouTube

The combination of Specification and Pageable in Spring Data JPA makes it super easy to fetch data with dynamic conditions and pagination. Let's walk through a step-by-step example using a Student entity.

1. Setting up the Project

Make sure you have the necessary dependencies in your pom.xml:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

2. Define the Entity

@Entity
public class Student {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    private String course;
    private Double grade;

    // getters, setters, etc.
}

3. Create the Repository:

Extend both JpaRepository and JpaSpecificationExecutor:

public interface StudentRepository extends JpaRepository<Student, Long>, JpaSpecificationExecutor<Student> {
}

4. Define Specifications

public class StudentSpecifications {
    
    public static Specification<Student> hasName(String name) {
        return (root, query, criteriaBuilder) -> criteriaBuilder.like(root.get("name"), "%" + name + "%");
    }

    public static Specification<Student> inCourse(String course) {
        return (root, query, criteriaBuilder) -> criteriaBuilder.equal(root.get("course"), course);
    }

    public static Specification<Student> hasGradeAbove(Double grade) {
        return (root, query, criteriaBuilder) -> criteriaBuilder.greaterThan(root.get("grade"), grade);
    }
}

5. Create a Service to Query with Pagination

@Service
public class StudentService {

    @Autowired
    private StudentRepository studentRepository;

    public Page<Student> findStudents(String name, String course, Double grade, Pageable pageable) {
        return studentRepository.findAll(
            Specification.where(StudentSpecifications.hasName(name))
                .and(StudentSpecifications.inCourse(course))
                .and(StudentSpecifications.hasGradeAbove(grade)),
            pageable
        );
    }
}

6. Using the Service:

You can use the PageRequest class to create an instance of Pageable:

@Autowired
private StudentService studentService;

public void fetchStudents() {
    Pageable firstPageWithTwoElements = PageRequest.of(0, 2);

    Page<Student> students = studentService.findStudents("John", "Mathematics", 80.0, firstPageWithTwoElements);
    
    // Access the list of students using students.getContent()
    for (Student student : students.getContent()) {
        // process each student
    }
}

7. Extending to Controllers

If you are integrating this into a web application, it's typical to pass pagination parameters like page, size, and sort from the client side:

@RestController
@RequestMapping("/students")
public class StudentController {

    @Autowired
    private StudentService studentService;

    @GetMapping
    public ResponseEntity<Page<Student>> getStudents(
            @RequestParam(required = false) String name,
            @RequestParam(required = false) String course,
            @RequestParam(required = false) Double grade,
            @RequestParam(defaultValue = "0") int page,
            @RequestParam(defaultValue = "10") int size,
            @RequestParam(defaultValue = "id,asc") String[] sort) {

        List<Sort.Order> orders = new ArrayList<>();
        for (String sortOrder : sort) {
            String[] _sort = sortOrder.split(",");
            orders.add(new Sort.Order(_sort[1].equalsIgnoreCase("desc") ? Sort.Direction.DESC : Sort.Direction.ASC, _sort[0]));
        }
        
        Page<Student> students = studentService.findStudents(name, course, grade, PageRequest.of(page, size, Sort.by(orders)));
        return new ResponseEntity<>(students, HttpStatus.OK);
    }
}

That's it! With this setup, you can create dynamic queries based on filters (like name, course, and grade) and fetch results with pagination and sorting.

Comments

Spring Boot 3 Paid Course Published for Free
on my Java Guides YouTube Channel

Subscribe to my YouTube Channel (165K+ subscribers):
Java Guides Channel

Top 10 My Udemy Courses with Huge Discount:
Udemy Courses - Ramesh Fadatare