如何使用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
相关产品推荐
相关产品推荐

