目录
使用specification进行动态分页查询案例演示,包含按时间的范围查询
最近在项目中遇到一个需求:对单个或者是多个条件进行分页查询(写在一个接口中),比如前端可能会传 name的模糊名,一个具体的字段,开始时间和结束时间,这四个参数可以组合传到你的后端接口来,所以我们需要对这种业务进行动态查询,而且查询出来的数据可能会非常的多,所以还需要对这些数据进行分页展示。所以我把这种业务情况称为动态条件分页查询,这里我使用的是jap中的specification的动态查询来实现这个功能。
使用specification进行动态分页查询的步骤如下:
Root:查询哪个表(表映射的java实体类,其中使用root对象获取属性的时候,参数传的是实体类中的属性名,不是表中的字段名) 相当于关键字 from
CriteriaQuery:查询哪些字段,排序是什么 相当于组合(order by . where )
CriteriaBuilder:条件之间是什么关系,如何生成一个查询条件,每一个查询条件都是什么类型(> between in...) = where
Predicate(Expression): 每一条查询条件的详细描述
root对象可以让我们从表中获取我们想要的列
/CriteriaBuilder where 设置各种条件 (> < in between......)
/query 组合(order by , where)
写下面的代码遇到的几个思考问题,希望可以帮助更好的理解这个specification的使用:
代码如下:
这个继承相当于mybatis中的dao或者是mapper层。
- public interface DtoJpa extends JpaRepository
, JpaSpecificationExecutor { - }
通过接收前端的查询条件来动态的拼接查询条件:理解了criteriaBuilder.like(name, "%"+dto.getName()+"%")这个拼接条件的代码的背后的SQL,那就理解的差不多了。
- /**
- * 通过Specification构建动态查询条件
- * @param dto 前端传过来的查询条件,因为可能参数比较多,所以我们创建了dto来承接这些查询条件,方便后期维护
- * @return specification 返回构建好的动态查询条件
- */
- public Specification
createSpecification(PageDto dto) { - Specification
specification = - new Specification
() { - @Override
- public Predicate toPredicate(Root root, CriteriaQuery criteriaQuery, CriteriaBuilder criteriaBuilder) {
- //构建查询条件 注意这里的 name startTime endTime 都是java实体类中的属性名
- Path
name = root.get("name"); - Path
startTime = root.get("startTime"); - Path
endTime = root.get("endTime"); - //用来存放查询条件
- ArrayList
list = new ArrayList<>(); - //进行模糊查询的条件拼接
- if (!StringUtils.isEmpty(dto.getName())) {
- //使用这个进行模糊查询,需要自己拼接百分号到查询条件
- //这里实际上就相当于 ... like name = "%dto.getName()%"
- list.add(criteriaBuilder.like(name, "%"+dto.getName()+"%"));
- }
- //使用specification进行时间范围查询
- if (dto.getStartTime() !=null && dto.getEndTime()!=null) {
- //开始时间
- list.add(criteriaBuilder.greaterThanOrEqualTo(startTime,dto.getStartTime()));
- list.add(criteriaBuilder.lessThanOrEqualTo(endTime,dto.getEndTime()));
- }
- // 组合条件 因为涉及动态查询,这个拼接的查询条件个数在上面的判断中已经确定,这里需要我们传一个数组过来,通过集合转数组的方法进行转换,不过需要的是Prediccate类型的定长数组
- Predicate and = criteriaBuilder.and(list.toArray(new Predicate[list.size()]));
- return and;
- }
- };
- return specification;
- }
-
- @autowire
- private DtoJpa dtoJpa;
-
- /**
- * 通过Specification进行条件分页查询
- * @param currentPage 当前页面
- * @param pageSize 每页存放的数据条数
- * @param dto 动态分页查询的条件
- * @return result 返回分页查询结果
- */
- public Page
page(Integer currentPage, Integer pageSize, PageDto dto) { -
- //这里的createSpecification方法就是上面我们构建动态条件的方法
- Specification
specification = this.createSpecification(dto); - //默认是从第1页开始,但是数据展示是从第0页开始的,所以需要减1判断
- Pageable pageable = PageRequest.of((currentPage - 1) > 0 ? (currentPage - 1) : 0, pageSize);
- //第一个参数是封装好的查询条件 这个findAll是jpa原生提供的,只需要继承JpaSpecificationExecutor就行
- Page
result = dtoJpa.findAll(specification, pageable); - return result;
- }
specification不支持分组等相关聚合函数的操作,但是支持排序 。是可以进行组合操作的,比如还可以在上面的返回值and上再进行拼接:
//对id字段进行降序排序
Order desc = criteriaQuery.desc(Id);//使用呢query对象对条件和组合进行拼接
return criteriaQuery.where(and).orderBy(desc).getRestriction();