0
0
SpringbootHow-ToBeginner · 4 min read

How to Implement Filtering in Spring Boot: Simple Guide

In Spring Boot, you can implement filtering by accepting query parameters in your controller and using them to build database queries with JpaSpecificationExecutor or ExampleMatcher. This allows dynamic filtering of data based on client requests.
📐

Syntax

Filtering in Spring Boot typically involves these parts:

  • Controller method that accepts filter parameters via @RequestParam.
  • Repository extending JpaSpecificationExecutor for dynamic queries.
  • Specification class that builds query conditions based on parameters.
java
public interface UserRepository extends JpaRepository<User, Long>, JpaSpecificationExecutor<User> {
}

public class UserSpecification implements Specification<User> {
    private final String name;

    public UserSpecification(String name) {
        this.name = name;
    }

    @Override
    public Predicate toPredicate(Root<User> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
        if (name == null || name.isEmpty()) {
            return cb.conjunction();
        }
        return cb.like(cb.lower(root.get("name")), "%" + name.toLowerCase() + "%");
    }
}

@GetMapping("/users")
public List<User> getUsers(@RequestParam(required = false) String name) {
    return userRepository.findAll(new UserSpecification(name));
}
💻

Example

This example shows a Spring Boot REST controller filtering users by their name using JpaSpecificationExecutor. If no name is given, it returns all users.

java
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.persistence.*;
import javax.persistence.criteria.*;
import java.util.List;

@Entity
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    // getters and setters
}

public interface UserRepository extends JpaRepository<User, Long>, JpaSpecificationExecutor<User> {
}

public class UserSpecification implements Specification<User> {
    private final String name;

    public UserSpecification(String name) {
        this.name = name;
    }

    @Override
    public Predicate toPredicate(Root<User> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
        if (name == null || name.isEmpty()) {
            return cb.conjunction();
        }
        return cb.like(cb.lower(root.get("name")), "%" + name.toLowerCase() + "%");
    }
}

@RestController
public class UserController {
    private final UserRepository userRepository;

    public UserController(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    @GetMapping("/users")
    public List<User> getUsers(@RequestParam(required = false) String name) {
        return userRepository.findAll(new UserSpecification(name));
    }
}
Output
[{"id":1,"name":"Alice"},{"id":2,"name":"Bob"}] // when no filter [{"id":1,"name":"Alice"}] // when ?name=ali
⚠️

Common Pitfalls

  • Not handling null or empty filter parameters causes errors or no results.
  • Using string concatenation in queries can cause SQL injection risks; use CriteriaBuilder methods instead.
  • Forgetting to extend JpaSpecificationExecutor in the repository disables specification filtering.
  • Not making filter parameters optional in the controller leads to bad requests if parameters are missing.
java
/* Wrong: Not handling null filter */
@Override
public Predicate toPredicate(Root<User> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
    return cb.like(root.get("name"), "%" + name + "%"); // throws NullPointerException if name is null
}

/* Right: Handle null safely */
@Override
public Predicate toPredicate(Root<User> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
    if (name == null || name.isEmpty()) {
        return cb.conjunction();
    }
    return cb.like(cb.lower(root.get("name")), "%" + name.toLowerCase() + "%");
}
📊

Quick Reference

Tips for filtering in Spring Boot:

  • Use @RequestParam(required = false) for optional filters.
  • Extend JpaSpecificationExecutor in your repository for flexible queries.
  • Create Specification classes to build dynamic predicates.
  • Always handle null or empty filter values to avoid errors.
  • Use CriteriaBuilder methods to prevent SQL injection.

Key Takeaways

Use JpaSpecificationExecutor and Specification to implement dynamic filtering in Spring Boot.
Make filter parameters optional and handle null or empty values safely.
Avoid string concatenation in queries; use CriteriaBuilder methods for security.
Extend your repository interface with JpaSpecificationExecutor to enable filtering.
Test filtering endpoints with and without parameters to ensure correct behavior.