1.适用范围
- 动态构建查询语句
- 支持所有的查询条件
- 支持子查询、连接查询、排序、分页
- 不支持自定义模型,无法做到将查询的结果封装为自定义的model,或类似
List<String>
的单列结果。只能返回dao定义的entity。
即,只支持select * from ...
1.1 JpaSpecificationExecutor
接口方法介绍
public interface JpaSpecificationExecutor<T> {
// 查询单个对象
Optional<T> findOne(@Nullable Specification<T> spec);
// 查询列表
List<T> findAll(@Nullable Specification<T> spec);
// 查询全部,分页;可以使用PageRequest#of(int, int, Sort)方法创建pageable对象实现分页+排序
Page<T> findAll(@Nullable Specification<T> spec, Pageable pageable);
// 查询全部,排序
List<T> findAll(@Nullable Specification<T> spec, Sort sort);
// 统计查询
long count(@Nullable Specification<T> spec);
}
1.2 Specification
类
Specification
类是一个用于构建动态查询条件的抽象类。
public interface Specification<T> {
Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder builder);
}
toPredicate
方法
Specification
类通过实现抽象方法Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder)
来构建动态查询条件,查询条件最终会生成一个查询条件对象(Predicate
对象)
Root
类:用于获取需要查询的列- 如:
root.get("lastName")
- 如:
CriteriaQuery
类:用于自定义查询方式- 如:
query.distinct(true).select(...).where(...).groupBy(...).having(...).getRestriction()
。最后通过getRestriction()
方法,可以获得一个Predicate
对象 - 详见
- 如:
CriteriaBuilder
类:构造查询条件(即,用于构建Predicate
对象),内部封装了很多的查询条件- 如:
criteriaBuilder.and(...)
、criteriaBuilder.equal(...)
、criteriaBuilder.greaterThan)
、criteriaBuilder.greaterThanOrEqualTo)
等,返回的结果是一个Predicate
对象 - 详见
- 如:
1.3 示例
1.3.1 简单示例
(1) 编写查询条件
@Test
public void testSimpleCbSpecification() {
Specification<RetPrdInfo> specification = (root, query, cb) -> {
// 等于
Predicate predicate1 = cb.equal(root.get("crToolRunMode"), "CLN");
// 不等于
Predicate predicate2 = cb.notEqual(root.get("prdSeqId"), "L000001");
// 大于
Predicate predicate3 = cb.greaterThan(root.get("prdQty"), 1);
// 大于等于
Predicate predicate4 = cb.greaterThanOrEqualTo(root.get("prdStdQty"), 1);
// 小于
Predicate predicate5 = cb.lessThan(root.get("prdQty"), 2);
// 小于等于
Predicate predicate6 = cb.lessThanOrEqualTo(root.get("prdStdQty"), 2);
// between
Timestamp startTime = Timestamp.valueOf(LocalDateTime.of(2021, 1, 1, 8, 0, 0));
Timestamp endTime = Timestamp.valueOf(LocalDateTime.of(2022, 1, 1, 8,