Demonstration cases and some precautions for using jap specification dynamic query

Table of contents

Learning background and usage steps

Dynamic pagination query case demonstration using specification, including range query by time


Learning background and usage steps

Recently, I encountered a requirement in a project: paging query for single or multiple conditions (written in an interface) , for example, the front end may pass the fuzzy name of name, a specific field, start time and end time, these four Multiple parameters can be combined and passed to your backend interface, so we need to perform dynamic query on this kind of business, and the queried data may be very large, so we also need to display the data in pages. So I call this business situation a dynamic conditional paging query. Here I use the dynamic query of specification in jap to realize this function.

The steps for dynamic pagination query using specification are as follows:

  • Inherit the interface JpaSpecificationExecutor (the generic type in it is the entity we need to query, for example, if we want to perform pagination query on users, then this user entity class needs to be mapped with a table in the database, and this generic type is mapped with the table in the database java entity class )
  • The implementation of incoming Specification (constructing query conditions): combined with lambda expressions
    • Root: which table to query (table-mapped java entity class, when the root object is used to obtain attributes, the parameter passed is the attribute name in the entity class, not the field name in the table) equivalent to the keyword from

    • CriteriaQuery: Which fields to query, what sort is equivalent to combination (order by . where)

    • CriteriaBuilder: What is the relationship between conditions, how to generate a query condition, what type is each query condition (> between in...) = where

    • Predicate (Expression): a detailed description of each query condition

    •             The root object allows us to get the columns we want from the table
                  /CriteriaBuilder where to set various conditions (> < in between...)
                  /query combination (order by , where)

  • Use the api provided by jpa to perform pagination query, just pass in the dynamic query condition you just built. Note that the generic type of the paging result returned here inherits the generic type from the JpaSpecificationExecutor interface.

Dynamic pagination query case demonstration using specification, including range query by time

A few thinking problems encountered in writing the following code, I hope it can help to better understand the use of this specification:

  1. Which generic type determines the result set of pagination query? The generic type that inherits the JobWarnDtoJpa interface determines the result of the pagination query.
  2. When root.get("name"); is the string inside the field name of the database or an attribute in the entity class mapped to the database? Attributes in entity classes.
  3. When using Specification to build dynamic query conditions, what is the generic type of Specification? --->The generic type in the paging results we need, for example, we need to display JobDto in paging, then the generic type of Specification is JobDto. (To understand: the query is performed from the table in the database, and then the third-party API used will help us encapsulate the queried data mapping into the entity class corresponding to the table , so using this technology requires the table and Entity classes are mapped (this is published by myself based on experience, if there is any problem, I hope you can correct me), of course, in order to query and display data more conveniently, in fact, we can use views to correspond to dto , so that when querying data This aspect can easily be combined with this third-party tool for query)
  4. When using criteriaBuilder to build dynamic query conditions, the first parameter in brackets is the Java entity class attribute corresponding to the table (the framework will help us map to the fields in the table), and the second parameter is the query we passed condition. For example, if you need to perform fuzzy query on name, then the first parameter is the name attribute of the entity class corresponding to the table, and the second parameter is the name (query condition) passed from other places

code show as below:

This inheritance is equivalent to the dao or mapper layer in mybatis.

public interface DtoJpa extends JpaRepository<Dto, Long>, JpaSpecificationExecutor<Dto> {
}

 Dynamic splicing of query conditions by receiving the query conditions of the front end: understand the SQL behind the code of the splicing conditions of criteriaBuilder.like(name, "%"+dto.getName()+"%") , then you can understand up.

/**
     * 通过Specification构建动态查询条件
     * @param dto 前端传过来的查询条件,因为可能参数比较多,所以我们创建了dto来承接这些查询条件,方便后期维护
     * @return specification 返回构建好的动态查询条件
     */
public Specification<Dto> createSpecification(PageDto dto) {
    Specification<Dto> specification =
        new Specification<Dto>() {
        @Override
        public Predicate toPredicate(Root root, CriteriaQuery criteriaQuery, CriteriaBuilder criteriaBuilder) {
            //构建查询条件   注意这里的  name startTime endTime 都是java实体类中的属性名
            Path<String> name = root.get("name");
            Path<Date> startTime = root.get("startTime");
            Path<Date> endTime = root.get("endTime");
			//用来存放查询条件
            ArrayList<Predicate> 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<Dto> page(Integer currentPage, Integer pageSize, PageDto dto) {

    //这里的createSpecification方法就是上面我们构建动态条件的方法
    Specification<Dto> specification = this.createSpecification(dto);
	//默认是从第1页开始,但是数据展示是从第0页开始的,所以需要减1判断
    Pageable pageable = PageRequest.of((currentPage - 1) > 0 ? (currentPage - 1) : 0, pageSize);
    //第一个参数是封装好的查询条件   这个findAll是jpa原生提供的,只需要继承JpaSpecificationExecutor就行
    Page<Dto> result = dtoJpa.findAll(specification, pageable);
    return result;
}

The specification does not support operations related to aggregate functions such as grouping, but sorting is supported . Combination operations can be performed, for example, splicing can also be performed on the above return value and:

                //Sort the id field in descending order
                Order desc = criteriaQuery.desc(Id);

                //Use the query object to stitch the conditions and combinations
                return criteriaQuery.where(and).orderBy(desc).getRestriction();

Guess you like

Origin blog.csdn.net/weixin_53142722/article/details/127417303