悠悠楠杉
使用CriteriaAPI实现JPA动态查询与分页
首先,我们需要定义一个服务类来封装查询逻辑。通过注入EntityManager,我们可以获取CriteriaBuilder实例,它是构建查询的核心工厂类。接着创建CriteriaQuery<T>对象,并指定返回类型为我们的文章实体。整个过程完全基于类和字段的引用,避免了字符串拼写错误,提升了代码的健壮性。
java
@Service
public class ArticleSearchService {
@PersistenceContext
private EntityManager entityManager;
public Page<Article> searchArticles(ArticleSearchCriteria criteria, Pageable pageable) {
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<Article> query = cb.createQuery(Article.class);
Root<Article> root = query.from(Article.class);
List<Predicate> predicates = new ArrayList<>();
if (criteria.getTitle() != null && !criteria.getTitle().trim().isEmpty()) {
predicates.add(cb.like(cb.lower(root.get("title")), "%" + criteria.getTitle().toLowerCase() + "%"));
}
if (criteria.getKeyword() != null && !criteria.getKeyword().trim().isEmpty()) {
predicates.add(cb.like(cb.lower(root.get("keyword")), "%" + criteria.getKeyword().toLowerCase() + "%"));
}
if (criteria.getDescription() != null && !criteria.getDescription().trim().isEmpty()) {
predicates.add(cb.like(cb.lower(root.get("description")), "%" + criteria.getDescription().toLowerCase() + "%"));
}
if (criteria.getContent() != null && !criteria.getContent().trim().isEmpty()) {
predicates.add(cb.like(cb.lower(root.get("content")), "%" + criteria.getContent().toLowerCase() + "%"));
}
query.where(predicates.toArray(new Predicate[0]));
TypedQuery<Article> typedQuery = entityManager.createQuery(query);
// 分页处理
typedQuery.setFirstResult((int) pageable.getOffset());
typedQuery.setMaxResults(pageable.getPageSize());
List<Article> results = typedQuery.getResultList();
long total = countTotal(criteria); // 自定义总数统计方法
return new PageImpl<>(results, pageable, total);
}
}
上述代码展示了如何将前端传入的搜索条件逐项转化为Predicate并加入到查询谓词列表中。只有当某个字段非空时,才会将其纳入查询条件,从而实现真正的“动态”拼接。这里特别注意对大小写的统一处理——通过lower()函数将数据库字段和参数都转为小写,确保模糊匹配时不区分大小写。
分页部分则借助Spring Data中的Pageable接口完成。我们将当前页的偏移量和每页大小设置到TypedQuery中,限制结果集范围。为了正确显示总记录数以支持分页导航,还需单独执行一次计数查询。该查询复用相同的过滤条件,但返回的是Long类型的总数。
值得一提的是,虽然Criteria API语法相对繁琐,但它带来的类型安全性是不可替代的。IDE可以实时提示字段名,重构时也能自动更新相关引用,极大降低了维护成本。此外,在面对更复杂的嵌套条件、关联查询或多表连接时,其表达能力远超注解式查询。
实际应用中,还可以进一步封装通用的查询构建器工具类,提取出共通的模糊匹配、区间判断、枚举过滤等逻辑,提升代码复用率。同时结合缓存机制,对于高频但低变动的查询结果进行适度缓存,可在不影响一致性的前提下显著提升响应速度。
总体来看,利用Criteria API实现JPA动态查询与分页,既保证了系统的灵活性,又兼顾了性能与可维护性。在内容检索、后台管理、数据分析等需要高度定制化查询的场景中,是一种成熟且可靠的解决方案。

