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

如何在Criteria API中更简洁处理可为空的可选搜索参数?

简化JPA Criteria查询中空参数处理的方案

先假设你的实体类示例如下(常见场景模拟):

@Entity
public class User {
    @Id
    private Long id;
    private String username;
    private Integer age;
    private LocalDate joinDate;
    // getter、setter 省略
}

以下是几种替代显式空值检查的简洁实现方案:

方案1:封装工具类复用判空+断言逻辑

把"判空+生成Predicate"的通用逻辑抽成工具方法,避免重复代码:

public class CriteriaUtils {
    // 字符串模糊查询处理
    public static Optional<Predicate> likeIfPresent(CriteriaBuilder cb, Expression<String> expr, String value) {
        return Optional.ofNullable(value)
                .filter(v -> !v.isBlank())
                .map(v -> cb.like(cb.lower(expr), "%" + v.toLowerCase() + "%"));
    }

    // 数值精确匹配处理
    public static <T extends Number> Optional<Predicate> equalIfPresent(CriteriaBuilder cb, Expression<T> expr, T value) {
        return Optional.ofNullable(value)
                .map(v -> cb.equal(expr, v));
    }

    // 日期范围起始值处理
    public static Optional<Predicate> greaterThanOrEqualToIfPresent(CriteriaBuilder cb, Expression<LocalDate> expr, LocalDate value) {
        return Optional.ofNullable(value)
                .map(v -> cb.greaterThanOrEqualTo(expr, v));
    }
}

查询代码中通过Stream收集非空断言:

public List<User> searchUsers(String username, Integer age, LocalDate startJoinDate) {
    CriteriaBuilder cb = entityManager.getCriteriaBuilder();
    CriteriaQuery<User> cq = cb.createQuery(User.class);
    Root<User> root = cq.from(User.class);

    List<Predicate> predicates = Stream.of(
            CriteriaUtils.likeIfPresent(cb, root.get("username"), username),
            CriteriaUtils.equalIfPresent(cb, root.get("age"), age),
            CriteriaUtils.greaterThanOrEqualToIfPresent(cb, root.get("joinDate"), startJoinDate)
    )
    .filter(Optional::isPresent)
    .map(Optional::get)
    .collect(Collectors.toList());

    cq.where(predicates.toArray(new Predicate[0]));
    return entityManager.createQuery(cq).getResultList();
}

方案2:用Java 8 Optional链式调用简化单参数处理

无需额外工具类,直接在查询代码中用Optional替代显式if判空:

public List<User> searchUsers(String username, Integer age, LocalDate startJoinDate) {
    CriteriaBuilder cb = entityManager.getCriteriaBuilder();
    CriteriaQuery<User> cq = cb.createQuery(User.class);
    Root<User> root = cq.from(User.class);

    List<Predicate> predicates = new ArrayList<>();

    // 处理用户名模糊查询
    Optional.ofNullable(username)
            .filter(s -> !s.isBlank())
            .map(s -> cb.like(cb.lower(root.get("username")), "%" + s.toLowerCase() + "%"))
            .ifPresent(predicates::add);

    // 处理年龄精确匹配
    Optional.ofNullable(age)
            .map(a -> cb.equal(root.get("age"), a))
            .ifPresent(predicates::add);

    // 处理注册起始日期
    Optional.ofNullable(startJoinDate)
            .map(date -> cb.greaterThanOrEqualTo(root.get("joinDate"), date))
            .ifPresent(predicates::add);

    cq.where(predicates.toArray(new Predicate[0]));
    return entityManager.createQuery(cq).getResultList();
}

方案3:借助第三方库(Querydsl)简化动态查询

如果项目允许引入第三方库,Querydsl可自动处理空参数,大幅简化代码:

先引入Maven依赖:

<dependency>
    <groupId>com.querydsl</groupId>
    <artifactId>querydsl-jpa</artifactId>
    <version>5.0.0</version>
</dependency>
<dependency>
    <groupId>com.querydsl</groupId>
    <artifactId>querydsl-apt</artifactId>
    <version>5.0.0</version>
    <scope>provided</scope>
</dependency>

编写查询代码:

public List<User> searchUsers(String username, Integer age, LocalDate startJoinDate) {
    JPAQueryFactory queryFactory = new JPAQueryFactory(entityManager);
    QUser qUser = QUser.user;

    return queryFactory.selectFrom(qUser)
            .where(
                Optional.ofNullable(username)
                        .filter(s -> !s.isBlank())
                        .map(s -> qUser.username.toLowerCase().contains(s.toLowerCase()))
                        .orElse(null),
                Optional.ofNullable(age).map(qUser.age::eq).orElse(null),
                Optional.ofNullable(startJoinDate).map(qUser.joinDate::goe).orElse(null)
            )
            .fetch();
}

方案优势对比

  • 移除了重复的空值判断代码,整体更简洁
  • 逻辑复用性强(工具类/第三方库方式)
  • 可读性提升,每个参数的查询逻辑清晰直观

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 00:42:34