Talk about jpa’s Dynamic Query



This paper mainly studies the dynamic query of jpa


Jpa absorbs criteria from hibernate, and uses criteria in combination with parsing url query syntax to realize end-to-end dynamic query.

The following shows the implementation in the next springside branch 4 release.

springside branch 4


 * Copyright (c) 2005, 2014
 * Licensed under the Apache License, Version 2.0 (the "License");
package org.springside.modules.persistence;

import java.util.Map;
import java.util.Map.Entry;

import org.apache.commons.lang3.StringUtils;


public class SearchFilter {

    public enum Operator {
        EQ, LIKE, GT, LT, GTE, LTE

    public String fieldName;
    public Object value;
    public Operator operator;

    public SearchFilter(String fieldName, Operator operator, Object value) {
        this.fieldName = fieldName;
        this.value = value;
        this.operator = operator;

     * searchParams中key的格式为OPERATOR_FIELDNAME
    public static Map<String, SearchFilter> parse(Map<String, Object> searchParams) {
        Map<String, SearchFilter> filters = Maps.newHashMap();

        for (Entry<String, Object> entry : searchParams.entrySet()) {
            // 过滤掉空值
            String key = entry.getKey();
            Object value = entry.getValue();
            if (StringUtils.isBlank((String) value)) {

            // 拆分operator与filedAttribute
            String[] names = StringUtils.split(key, "_");
            if (names.length != 2) {
                throw new IllegalArgumentException(key + " is not a valid search filter name");
            String filedName = names[1];
            Operator operator = Operator.valueOf(names[0]);

            // 创建searchFilter
            SearchFilter filter = new SearchFilter(filedName, operator, value);
            filters.put(key, filter);

        return filters;

Several operators EQ, LIKE, GT, LT, GTE, LTE are defined here.
You can receive query criteria from mvc controller and convert them into SearchFilter.


 * Copyright (c) 2005, 2014
 * Licensed under the Apache License, Version 2.0 (the "License");
package org.springside.modules.persistence;

import java.util.Collection;
import java.util.List;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Path;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;

import org.apache.commons.lang3.StringUtils;
import org.springside.modules.utils.Collections3;


public class DynamicSpecifications {

    public static <T> Specification<T> bySearchFilter(final Collection<SearchFilter> filters, final Class<T> entityClazz) {
        return new Specification<T>() {
            public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder builder) {
                if (Collections3.isNotEmpty(filters)) {

                    List<Predicate> predicates = Lists.newArrayList();
                    for (SearchFilter filter : filters) {
                        // nested path translate, 如Task的名为""的filedName, 转换为Task.user.name属性
                        String[] names = StringUtils.split(filter.fieldName, ".");
                        Path expression = root.get(names[0]);
                        for (int i = 1; i < names.length; i++) {
                            expression = expression.get(names[i]);

                        // logic operator
                        switch (filter.operator) {
                        case EQ:
                            predicates.add(builder.equal(expression, filter.value));
                        case LIKE:
                            predicates.add(, "%" + filter.value + "%"));
                        case GT:
                            predicates.add(builder.greaterThan(expression, (Comparable) filter.value));
                        case LT:
                            predicates.add(builder.lessThan(expression, (Comparable) filter.value));
                        case GTE:
                            predicates.add(builder.greaterThanOrEqualTo(expression, (Comparable) filter.value));
                        case LTE:
                            predicates.add(builder.lessThanOrEqualTo(expression, (Comparable) filter.value));

                    // 将所有条件用 and 联合起来
                    if (!predicates.isEmpty()) {
                        return builder.and(predicates.toArray(new Predicate[predicates.size()]));

                return builder.conjunction();

The main purpose here is to convert the query criteria constructed by SearchFilter into the corresponding Predicate and then into jpa Specification to complete the conversion of dynamic query criteria.


Using springside’s DynamicSpecifications and mapping mvc parameters to SearchFilter, you can also implement a set of end-to-end dynamic queries yourself.

Note springside has deleted these codes in the latest version and has to find them in branch 4.