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
Please enter at least three characters to search
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

Last call! Secure your stack and shape the future! Help dev teams across the globe navigate their software supply chain security challenges.

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workloads.

Releasing software shouldn't be stressful or risky. Learn how to leverage progressive delivery techniques to ensure safer deployments.

Avoid machine learning mistakes and boost model performance! Discover key ML patterns, anti-patterns, data strategies, and more.

Related

  • DGS GraphQL and Spring Boot
  • Beyond Linguistics: Real-Time Domain Event Mapping with WebSocket and Spring Boot
  • Issue and Present Verifiable Credentials With Spring Boot and Android
  • Java's Quiet Revolution: Thriving in the Serverless Kubernetes Era

Trending

  • Docker Model Runner: Streamlining AI Deployment for Developers
  • Kubeflow: Driving Scalable and Intelligent Machine Learning Systems
  • Revolutionizing Financial Monitoring: Building a Team Dashboard With OpenObserve
  • Building Enterprise-Ready Landing Zones: Beyond the Initial Setup
  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
Likes (2)
Comment
Save
Tweet
Share
9.9K 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

  • DGS GraphQL and Spring Boot
  • Beyond Linguistics: Real-Time Domain Event Mapping with WebSocket and Spring Boot
  • Issue and Present Verifiable Credentials With Spring Boot and Android
  • Java's Quiet Revolution: Thriving in the Serverless Kubernetes Era

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and 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:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!