在Java持久层开发中,当业务需要支持多维度动态筛选,同时要求返回分页结果时,JPA Criteria API是比JPQL更灵活的查询构建方案,它可以在运行时动态拼接查询条件,还能和分页参数无缝结合。

JPA Criteria API基础概念
JPA Criteria API是JPA 2.0引入的类型安全查询API,它通过面向对象的方式构建查询,避免了拼接字符串带来的SQL注入风险,也解决了JPQL无法动态适配条件变化的问题。核心组件包括CriteriaBuilder、CriteriaQuery、Root等。
核心组件说明
CriteriaBuilder:用于创建查询的各个部分,比如条件表达式、排序规则等CriteriaQuery:代表整个查询对象,用于设置查询的返回类型、筛选条件、排序等Root:代表查询的根实体,通过它可以获取实体的各个属性,用于构建条件
复杂筛选条件构建
复杂筛选通常指条件不是固定的,会根据前端传入的参数动态增减,比如用户列表查询可能同时支持按用户名、状态、创建时间范围筛选,部分参数可能为空不需要加入查询。
动态条件拼接示例
假设我们有一个User实体,包含id、username、status、createTime属性,现在需要构建动态筛选条件:
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import java.util.ArrayList;
import java.util.List;
public class UserQueryCondition {
private String username;
private Integer status;
private Long startTime;
private Long endTime;
// getter和setter省略
/**
* 构建动态筛选条件
*/
public Predicate buildPredicate(Root<User> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
List<Predicate> predicates = new ArrayList<>();
// 用户名模糊筛选,非空时加入条件
if (username != null && !username.trim().isEmpty()) {
predicates.add(criteriaBuilder.like(root.get("username"), "%" + username + "%"));
}
// 状态精确筛选,非空时加入条件
if (status != null) {
predicates.add(criteriaBuilder.equal(root.get("status"), status));
}
// 创建时间范围筛选
if (startTime != null) {
predicates.add(criteriaBuilder.ge(root.get("createTime"), startTime));
}
if (endTime != null) {
predicates.add(criteriaBuilder.le(root.get("createTime"), endTime));
}
// 将所有条件用and连接
return criteriaBuilder.and(predicates.toArray(new Predicate[0]));
}
}
结合后端分页实现完整查询
后端分页需要返回指定页的数据以及总条数,Spring Data JPA提供了Pageable和Page接口来支持分页,我们可以将Criteria API构建的查询和分页参数结合。
Repository层实现
自定义Repository接口,继承JpaSpecificationExecutor来获得支持Specification的查询能力:
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.repository.PagingAndSortingRepository;
public interface UserRepository extends PagingAndSortingRepository<User, Long>, JpaSpecificationExecutor<User> {
}
Service层实现完整分页查询
在Service中组合筛选条件和分页参数,调用Repository的方法获取分页结果:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
/**
* 带复杂筛选的后端分页查询
*/
public Page<User> queryUserByCondition(UserQueryCondition condition, Pageable pageable) {
// 构建Specification,将动态条件适配到Spring Data JPA的查询中
Specification<User> specification = (root, query, criteriaBuilder) -> {
// 调用条件构建方法生成筛选条件
return condition.buildPredicate(root, query, criteriaBuilder);
};
// 执行分页查询,返回Page对象包含数据和分页信息
return userRepository.findAll(specification, pageable);
}
}
Controller层接收参数调用
Controller层接收前端传入的筛选参数和分页参数,调用Service返回结果:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/users")
public Page<User> listUsers(
@RequestParam(required = false) String username,
@RequestParam(required = false) Integer status,
@RequestParam(required = false) Long startTime,
@RequestParam(required = false) Long endTime,
@RequestParam(defaultValue = "0") Integer page,
@RequestParam(defaultValue = "10") Integer size) {
// 构建查询条件对象
UserQueryCondition condition = new UserQueryCondition();
condition.setUsername(username);
condition.setStatus(status);
condition.setStartTime(startTime);
condition.setEndTime(endTime);
// 构建分页参数,默认按创建时间倒序
Pageable pageable = PageRequest.of(page, size, Sort.by(Sort.Direction.DESC, "createTime"));
return userService.queryUserByCondition(condition, pageable);
}
}
注意事项
- 构建模糊查询时,需要对传入的参数做非空校验,避免拼接出
like '%%'这种无意义的条件,影响查询性能 - 如果筛选条件包含关联实体的属性,需要通过
root.join()方法关联对应的实体后再获取属性 - 分页参数中
page默认从0开始,和前端常见的从1开始的页码需要做转换适配 - 如果查询需要返回自定义DTO而不是实体,可以通过
CriteriaQuery.multiselect()方法指定返回的字段,构造自定义结果
JPA_Criteria_API复杂筛选后端分页Spring_Data_JPA修改时间:2026-07-07 00:30:42