DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Low-Code Development: Leverage low and no code to streamline your workflow so that you can focus on higher priorities.

DZone Security Research: Tell us your top security strategies in 2024, influence our research, and enter for a chance to win $!

Launch your software development career: Dive head first into the SDLC and learn how to build high-quality software and teams.

Open Source Migration Practices and Patterns: Explore key traits of migrating open-source software and its impact on software development.

Related

  • GenAI: Spring Boot Integration With LocalAI for Code Conversion
  • Custom Health Checks in Spring Boot
  • The Magic of Quarkus With Vert.x in Reactive Programming
  • Tackling Records in Spring Boot

Trending

  • Next-Gen Lie Detector: Stack Selection
  • Outsmarting Cyber Threats: How Large Language Models Can Revolutionize Email Security
  • Tenv v2.0: The Importance of Explicit Behavior for Version Manager
  • Operational Excellence Best Practices
  1. DZone
  2. Coding
  3. Frameworks
  4. Dynamic Query Building Spring Boot With JPA Criteria Queries

Dynamic Query Building Spring Boot With JPA Criteria Queries

In this article, we’ll explore a flexible and reusable framework that allows developers to construct complex queries effortlessly.

By 
Bhanuprakash Jirra user avatar
Bhanuprakash Jirra
·
May. 16, 24 · Tutorial
Like (2)
Save
Tweet
Share
3.5K Views

Join the DZone community and get the full member experience.

Join For Free

Dynamic query building is a critical aspect of modern application development, especially in scenarios where the search criteria are not known at compile time. In this publication, let's deep dive into the world of dynamic query building in Spring Boot applications using JPA criteria queries. We’ll explore a flexible and reusable framework that allows developers to construct complex queries effortlessly.

Explanation of Components

Criteria Interface

  • The Criteria interface serves as the foundation for our framework. It extends Specification<T> and provides a standardized way to build dynamic queries.
  • By implementing the toPredicate method, the Criteria interface enables the construction of predicates based on the specified criteria.
Java
 
package com.core.jpa;

import java.util.ArrayList;
import java.util.List;

import org.springframework.data.jpa.domain.Specification;

import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.CriteriaQuery;
import jakarta.persistence.criteria.Predicate;
import jakarta.persistence.criteria.Root;

public class Criteria<T> implements Specification<T> {

    private static final long serialVersionUID = 1L;
    
    private transient List<Criterion> criterions = new ArrayList<>();

    @Override
    public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder builder) {
        if (!criterions.isEmpty()) {
            List<Predicate> predicates = new ArrayList<>();
            for (Criterion c : criterions) {
                predicates.add(c.toPredicate(root, query, builder));
            }

            if (!predicates.isEmpty()) {
                return builder.and(predicates.toArray(new Predicate[predicates.size()]));
            }
        }
        return builder.conjunction();
    }

    public void add(Criterion criterion) {
        if (criterion != null) {
            criterions.add(criterion);
        }
    }

}


Criterion Interface

  • The Criterion interface defines the contract for building individual predicates. It includes the toPredicate method, which is implemented by various classes to create specific predicates such as equals, not equals, like, etc.
Java
 
public interface Criterion {
    public enum Operator {
        EQ, IGNORECASEEQ, NE, LIKE, GT, LT, GTE, LTE, AND, OR, ISNULL
    }

    public Predicate toPredicate(Root<?> root, CriteriaQuery<?> query, CriteriaBuilder builder);
}


LogicalExpression Class

  • The LogicalExpression class facilitates the combination of multiple criteria using logical operators such as AND and OR.
  • By implementing the toPredicate method, this class allows developers to create complex query conditions by chaining together simple criteria.
Java
 
public class LogicalExpression implements Criterion {  
    private Criterion[] criterion;  
    private Operator operator;      
  
    public LogicalExpression(Criterion[] criterions, Operator operator) {  
        this.criterion = criterions;  
        this.operator = operator;  
    }  
  
    @Override
    public Predicate toPredicate(Root<?> root, CriteriaQuery<?> query, CriteriaBuilder builder) {  
        List<Predicate> predicates = new ArrayList<>();  
        for(int i=0;i<this.criterion.length;i++){  
            predicates.add(this.criterion[i].toPredicate(root, query, builder));  
        }  
        
        if(null != operator && operator.equals(Criterion.Operator.OR)) {
            return builder.or(predicates.toArray(new Predicate[predicates.size()]));  
        } 
        
        return null;
    }  
}


Restrictions Class

  • The Restrictions class provides a set of static methods for creating instances of SimpleExpression and LogicalExpression.
  • These methods offer convenient ways to build simple and complex criteria, making it easier for developers to construct dynamic queries.
Java
 
public class Restrictions {
    private Restrictions() {
    }

    public static SimpleExpression eq(String fieldName, Object value, boolean ignoreNull) {
        if (ignoreNull && (ObjectUtils.isEmpty(value)))
            return null;
        return new SimpleExpression(fieldName, value, Operator.EQ);
    }

    public static SimpleExpression ne(String fieldName, Object value, boolean ignoreNull) {
        if (ignoreNull && (ObjectUtils.isEmpty(value)))
            return null;
        return new SimpleExpression(fieldName, value, Operator.NE);
    }

    public static SimpleExpression like(String fieldName, String value, boolean ignoreNull) {
        if (ignoreNull && (ObjectUtils.isEmpty(value)))
            return null;
        return new SimpleExpression(fieldName, value.toUpperCase(), Operator.LIKE);
    }

    public static SimpleExpression gt(String fieldName, Object value, boolean ignoreNull) {
        if (ignoreNull && (ObjectUtils.isEmpty(value)))
            return null;
        return new SimpleExpression(fieldName, value, Operator.GT);
    }

    public static SimpleExpression lt(String fieldName, Object value, boolean ignoreNull) {
        if (ignoreNull && (ObjectUtils.isEmpty(value)))
            return null;
        return new SimpleExpression(fieldName, value, Operator.LT);
    }

    public static SimpleExpression gte(String fieldName, Object value, boolean ignoreNull) {
        if (ignoreNull && (ObjectUtils.isEmpty(value)))
            return null;
        return new SimpleExpression(fieldName, value, Operator.GTE);
    }

    public static SimpleExpression lte(String fieldName, Object value, boolean ignoreNull) {
        if (ignoreNull && (ObjectUtils.isEmpty(value)))
            return null;
        return new SimpleExpression(fieldName, value, Operator.LTE);
    }

    public static SimpleExpression isNull(String fieldName, boolean ignoreNull) {
        if (ignoreNull)
            return null;
        return new SimpleExpression(fieldName, null, Operator.ISNULL);
    }

    public static LogicalExpression and(Criterion... criterions) {
        return new LogicalExpression(criterions, Operator.AND);
    }

    public static LogicalExpression or(Criterion... criterions) {
        return new LogicalExpression(criterions, Operator.OR);
    }

    public static <E> LogicalExpression in(String fieldName, Collection<E> value, boolean ignoreNull) {
        if (ignoreNull && CollectionUtils.isEmpty(value))
            return null;

        SimpleExpression[] ses = new SimpleExpression[value.size()];
        int i = 0;
        for (Object obj : value) {
            if(obj instanceof String) {
                ses[i] = new SimpleExpression(fieldName, String.valueOf(obj), Operator.IGNORECASEEQ);
            } else {
                ses[i] = new SimpleExpression(fieldName, obj, Operator.EQ);
            }
            i++;
        }
        return new LogicalExpression(ses, Operator.OR);
    }

    public static Long convertToLong(Object o) {
        String stringToConvert = String.valueOf(o);
        if (!"null".equals(stringToConvert)) {
            return Long.parseLong(stringToConvert);
        } else {
            return Long.valueOf(0);
        }
    }
}


SimpleExpression Class

  • The SimpleExpression class represents simple expressions with various operators such as equals, not equals, like, greater than, less than, etc.
  • By implementing the toPredicate method, this class translates simple expressions into JPA criteria predicates, allowing for precise query construction.
  • The SimpleExpression class represents simple expressions with various operators such as equals, not equals, like, greater than, less than, etc.
  • By implementing the toPredicate method, this class translates simple expressions into JPA criteria predicates, allowing for precise query construction.
Java
 
public class SimpleExpression implements Criterion {
    private String fieldName;
    private Object value;
    private Operator operator;

    protected SimpleExpression(String fieldName, Object value, Operator operator) {
        this.fieldName = fieldName;
        this.value = value;
        this.operator = operator;
    }

    @Override
    @SuppressWarnings({ "rawtypes", "unchecked" })
    public Predicate toPredicate(Root<?> root, CriteriaQuery<?> query, CriteriaBuilder builder) {
        Path expression = null;
        if (fieldName.contains(".")) {
            String[] names = StringUtils.split(fieldName, ".");
            if(names!=null && names.length>0) {
                expression = root.get(names[0]);
                for (int i = 1; i < names.length; i++) {
                    expression = expression.get(names[i]);
                }
            }
        } else {
            expression = root.get(fieldName);
        }

        switch (operator) {
            case EQ:
                return builder.equal(expression, value);
            case IGNORECASEEQ:
                return builder.equal(builder.upper(expression), value.toString().toUpperCase());
            case NE:
                return builder.notEqual(expression, value);
            case LIKE:
                return builder.like(builder.upper(expression), value.toString().toUpperCase() + "%");
            case LT:
                return builder.lessThan(expression, (Comparable) value);
            case GT:
                return builder.greaterThan(expression, (Comparable) value);
            case LTE:
                return builder.lessThanOrEqualTo(expression, (Comparable) value);
            case GTE:
                return builder.greaterThanOrEqualTo(expression, (Comparable) value);
            case ISNULL:
                return builder.isNull(expression);
            default:
                return null;
        }
    }
}


Usage Example

Suppose we have a User entity and a corresponding UserRepository interface defined in our Spring Boot application:

Java
 
@Entity
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;
    private int age;
    private double salary;
    
    // Getters and setters
}

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


With these entities in place, let’s demonstrate how to use our dynamic query-building framework to retrieve a list of users based on certain search criteria:

Java
 
Criteria<User> criteria = new Criteria<>();
criteria.add(Restrictions.eq("age", 25, true));
criteria.add(Restrictions.like("name", "John", true));
criteria.add(Restrictions.or(
    Restrictions.gt("salary", 50000, true),
    Restrictions.isNull("salary", null, false)
));

List<User> users = userRepository.findAll(criteria);


In this example, we construct a dynamic query using the Criteria interface and various Restrictions provided by our framework. We specify criteria such as age equals 25, name contains "John", and salary greater than 50000 or null. Finally, we use the UserRepository to execute the query and retrieve the matching users.

Conclusion

Dynamic query building with JPA criteria queries in Spring Boot applications empowers developers to create sophisticated queries tailored to their specific needs. By leveraging the framework outlined in this publication, developers can streamline the process of constructing dynamic queries and enhance the flexibility and efficiency of their applications.

Additional Resources

  • Spring Data JPA Documentation
Spring Boot

Opinions expressed by DZone contributors are their own.

Related

  • GenAI: Spring Boot Integration With LocalAI for Code Conversion
  • Custom Health Checks in Spring Boot
  • The Magic of Quarkus With Vert.x in Reactive Programming
  • Tackling Records in Spring Boot

Partner Resources


Comments

ABOUT US

  • About DZone
  • Send feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends: