You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用Querydsl实现支持比较运算符与关联查询的REST接口过滤功能

Spring Data Querydsl 多场景过滤查询实现方案

前置准备

首先确保项目引入Querydsl相关依赖并配置APT插件用于自动生成查询实体类(Q类),Maven配置示例:

<dependencies>
    <!--Querydsl核心依赖-->
    <dependency>
        <groupId>com.querydsl</groupId>
        <artifactId>querydsl-jpa</artifactId>
        <classifier>jakarta</classifier> <!-- 非SpringBoot3用hibernate分类器 -->
    </dependency>
    <dependency>
        <groupId>com.querydsl</groupId>
        <artifactId>querydsl-apt</artifactId>
        <classifier>jakarta</classifier>
        <scope>provided</scope>
    </dependency>
</dependencies>

<build>
    <plugins>
        <!-- APT插件用于编译时生成Q类 -->
        <plugin>
            <groupId>com.mysema.maven</groupId>
            <artifactId>apt-maven-plugin</artifactId>
            <version>1.1.3</version>
            <executions>
                <execution>
                    <goals>
                        <goal>process</goal>
                    </goals>
                    <configuration>
                        <outputDirectory>target/generated-sources/java</outputDirectory>
                        <processor>com.querydsl.apt.jpa.JPAAnnotationProcessor</processor>
                    </configuration>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

配置完成后执行一次compile命令,会自动生成QAuthor、QBook查询类。


核心实现

1. 实体类补充完整配置

@Entity
@Data // lombok注解,省略getter/setter
public class Author {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    private Integer age;

    @ManyToMany
    @JoinTable(name = "author_book",
            joinColumns = @JoinColumn(name = "author_id"),
            inverseJoinColumns = @JoinColumn(name = "book_id"))
    private Set<Book> books;
}

@Entity
@Data
public class Book {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;

    @ManyToMany(mappedBy = "books")
    private Set<Author> authors;
}

2. 自定义查询条件解析工具类

该类用于动态解析请求参数中的运算符和关联字段,生成Querydsl Predicate:

import com.querydsl.core.types.Predicate;
import com.querydsl.core.types.dsl.PathBuilder;
import org.springframework.util.CollectionUtils;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class QuerydslPredicateParser {
    // 运算符匹配规则,优先级从高到低
    private static final Pattern OP_PATTERN = Pattern.compile("(>=|<=|!=|>|<|:|=)");
    private static final PathBuilder<Author> PATH_BUILDER = new PathBuilder<>(Author.class, "author");

    public static Predicate parse(Map<String, String[]> params) {
        if (CollectionUtils.isEmpty(params)) return null;
        com.querydsl.core.types.dsl.BooleanExpression expression = null;

        for (Map.Entry<String, String[]> entry : params.entrySet()) {
            String key = entry.getKey();
            String value = entry.getValue()[0];
            String fieldName;
            String op;

            // 处理filter参数格式:filter=age>30
            if ("filter".equals(key)) {
                Matcher matcher = OP_PATTERN.matcher(value);
                if (!matcher.find()) continue;
                op = matcher.group();
                fieldName = value.substring(0, matcher.start());
                value = value.substring(matcher.end());
            } else {
                // 处理直接参数格式:age>30
                Matcher matcher = OP_PATTERN.matcher(key);
                if (!matcher.find()) continue;
                op = matcher.group();
                fieldName = key.substring(0, matcher.start());
            }

            // 构建字段路径,支持关联字段如books.name
            PathBuilder<Object> fieldPath = getFieldPath(fieldName);
            // 拼接查询条件
            com.querydsl.core.types.dsl.BooleanExpression tempExp = switch (op) {
                case ">" -> fieldPath.asInt().gt(Integer.parseInt(value));
                case "<" -> fieldPath.asInt().lt(Integer.parseInt(value));
                case ">=" -> fieldPath.asInt().goe(Integer.parseInt(value));
                case "<=" -> fieldPath.asInt().loe(Integer.parseInt(value));
                case "!=" -> fieldPath.ne(value);
                case ":", "=" -> fieldPath.asString().eq(value);
                default -> null;
            };
            if (tempExp != null) {
                expression = expression == null ? tempExp : expression.and(tempExp);
            }
        }
        return expression;
    }

    // 递归获取关联字段路径
    private static PathBuilder<Object> getFieldPath(String fieldName) {
        if (!fieldName.contains(".")) {
            return PATH_BUILDER.get(fieldName);
        }
        String[] fields = fieldName.split("\\.");
        PathBuilder<Object> path = PATH_BUILDER.get(fields[0]);
        for (int i = 1; i < fields.length; i++) {
            path = path.get(fields[i]);
        }
        return path;
    }
}

3. Repository层定义

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.querydsl.QuerydslPredicateExecutor;

public interface AuthorRepository extends JpaRepository<Author, Long>, QuerydslPredicateExecutor<Author> {
}

4. Controller层实现

import jakarta.servlet.http.HttpServletRequest;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.querydsl.core.types.Predicate;
import java.util.List;

@RestController
@RequestMapping("/api/author")
public class AuthorController {
    private final AuthorRepository authorRepository;

    public AuthorController(AuthorRepository authorRepository) {
        this.authorRepository = authorRepository;
    }

    @GetMapping
    public List<Author> getAuthors(HttpServletRequest request) {
        Predicate predicate = QuerydslPredicateParser.parse(request.getParameterMap());
        if (predicate == null) {
            return authorRepository.findAll();
        }
        return (List<Author>) authorRepository.findAll(predicate);
    }
}

接口验证

支持以下请求格式:

  • 查询年龄大于30的作者:GET "http://localhost:8080/api/author?filter=age>30" 或 GET "http://localhost:8080/api/author?age>30"
  • 查询名为HarryPotter的图书的所有作者:GET "http://localhost:8080/api/author?filter=books.name:HarryPotter" 或 GET "http://localhost:8080/api/author?books.name=HarryPotter"

内容的提问来源于stack exchange,提问作者altindalorcun

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.04 02:18:03