• 使用jap的specification动态查询的演示案例与一些注意事项


    目录

    学习背景与使用步骤

    使用specification进行动态分页查询案例演示,包含按时间的范围查询


    学习背景与使用步骤

    最近在项目中遇到一个需求:对单个或者是多个条件进行分页查询(写在一个接口中),比如前端可能会传  name的模糊名,一个具体的字段,开始时间和结束时间,这四个参数可以组合传到你的后端接口来,所以我们需要对这种业务进行动态查询,而且查询出来的数据可能会非常的多,所以还需要对这些数据进行分页展示。所以我把这种业务情况称为动态条件分页查询,这里我使用的是jap中的specification的动态查询来实现这个功能。

    使用specification进行动态分页查询的步骤如下:

    • 继承接口JpaSpecificationExecutor(里面的泛型是我们需要查询的实体,比如我们要对用户进行分页查询,那么这个user实体类需要和数据库中的一张表进行映射,这个泛型就是与数据库中表映射的java实体类
    • 传入Specification的实现(构建查询条件): 结合lambda表达式
      • Root:查询哪个表(表映射的java实体类,其中使用root对象获取属性的时候,参数传的是实体类中的属性名,不是表中的字段名) 相当于关键字 from

      • CriteriaQuery:查询哪些字段,排序是什么 相当于组合(order by . where )

      • CriteriaBuilder:条件之间是什么关系,如何生成一个查询条件,每一个查询条件都是什么类型(> between in...) = where

      • Predicate(Expression): 每一条查询条件的详细描述

      •             root对象可以让我们从表中获取我们想要的列
                    /CriteriaBuilder where 设置各种条件  (> < in between......)
                    /query  组合(order by , where)

    • 使用jpa提供的api进行分页查询,把刚刚构建好的动态查询条件传入就行,注意这里返回的分页结果的泛型是继承JpaSpecificationExecutor这个接口中的泛型

    使用specification进行动态分页查询案例演示,包含按时间的范围查询

    写下面的代码遇到的几个思考问题,希望可以帮助更好的理解这个specification的使用:

    1. 分页查询的结果集是由哪个泛型决定的?继承JobWarnDtoJpa这个接口的泛型决定了分页查询的结果。
    2. root.get("name");的时候里面的字符串是数据库的字段名还是与数据库映射的实体类中的属性?实体类中的属性。
    3. 使用Specification构建动态查询条件的时候,Specification的泛型是什么?--->我们需要的分页结果中的泛型,比如我们需要对JobDto进行分页展示,那么Specification的泛型就是JobDto。(要明白:查询是从数据库中的表进行查询的,然后使用的第三方的api会帮我们把查询出来的数据映射封装到与表对应的实体类中,所以使用这种技术是需要表与实体类进行映射(这个是个人自己根据经验发表的哈,如果有问题希望大佬指正),当然为了更加方便的查询展示数据,实际上我们是可以使用视图来与dto进行对应,这样在查询数据这一方面就可以轻松的结合这种第三方工具来进行查询了)
    4. 使用criteriaBuilder构建动态查询条件的时候,括号中的第一个参数是与表对应的Java实体类属性(框架会帮我们映射到表中的字段中去),第二个参数是我们传过来的查询条件。比如需要对name进行模糊查询,那么第一个参数就是与表对应的实体类的name属性,第二个参数就是其他地方传过来的name(查询条件)

    代码如下:

    这个继承相当于mybatis中的dao或者是mapper层。

    1. public interface DtoJpa extends JpaRepository, JpaSpecificationExecutor {
    2. }

     通过接收前端的查询条件来动态的拼接查询条件:理解了criteriaBuilder.like(name, "%"+dto.getName()+"%")这个拼接条件的代码的背后的SQL,那就理解的差不多了。

    1. /**
    2. * 通过Specification构建动态查询条件
    3. * @param dto 前端传过来的查询条件,因为可能参数比较多,所以我们创建了dto来承接这些查询条件,方便后期维护
    4. * @return specification 返回构建好的动态查询条件
    5. */
    6. public Specification createSpecification(PageDto dto) {
    7. Specification specification =
    8. new Specification() {
    9. @Override
    10. public Predicate toPredicate(Root root, CriteriaQuery criteriaQuery, CriteriaBuilder criteriaBuilder) {
    11. //构建查询条件 注意这里的 name startTime endTime 都是java实体类中的属性名
    12. Path name = root.get("name");
    13. Path startTime = root.get("startTime");
    14. Path endTime = root.get("endTime");
    15. //用来存放查询条件
    16. ArrayList list = new ArrayList<>();
    17. //进行模糊查询的条件拼接
    18. if (!StringUtils.isEmpty(dto.getName())) {
    19. //使用这个进行模糊查询,需要自己拼接百分号到查询条件
    20. //这里实际上就相当于 ... like name = "%dto.getName()%"
    21. list.add(criteriaBuilder.like(name, "%"+dto.getName()+"%"));
    22. }
    23. //使用specification进行时间范围查询
    24. if (dto.getStartTime() !=null && dto.getEndTime()!=null) {
    25. //开始时间
    26. list.add(criteriaBuilder.greaterThanOrEqualTo(startTime,dto.getStartTime()));
    27. list.add(criteriaBuilder.lessThanOrEqualTo(endTime,dto.getEndTime()));
    28. }
    29. // 组合条件 因为涉及动态查询,这个拼接的查询条件个数在上面的判断中已经确定,这里需要我们传一个数组过来,通过集合转数组的方法进行转换,不过需要的是Prediccate类型的定长数组
    30. Predicate and = criteriaBuilder.and(list.toArray(new Predicate[list.size()]));
    31. return and;
    32. }
    33. };
    34. return specification;
    35. }
    36. @autowire
    37. private DtoJpa dtoJpa;
    38. /**
    39. * 通过Specification进行条件分页查询
    40. * @param currentPage 当前页面
    41. * @param pageSize 每页存放的数据条数
    42. * @param dto 动态分页查询的条件
    43. * @return result 返回分页查询结果
    44. */
    45. public Page page(Integer currentPage, Integer pageSize, PageDto dto) {
    46. //这里的createSpecification方法就是上面我们构建动态条件的方法
    47. Specification specification = this.createSpecification(dto);
    48. //默认是从第1页开始,但是数据展示是从第0页开始的,所以需要减1判断
    49. Pageable pageable = PageRequest.of((currentPage - 1) > 0 ? (currentPage - 1) : 0, pageSize);
    50. //第一个参数是封装好的查询条件 这个findAll是jpa原生提供的,只需要继承JpaSpecificationExecutor就行
    51. Page result = dtoJpa.findAll(specification, pageable);
    52. return result;
    53. }

    specification不支持分组等相关聚合函数的操作,但是支持排序 。是可以进行组合操作的,比如还可以在上面的返回值and上再进行拼接:

                    //对id字段进行降序排序
                    Order desc = criteriaQuery.desc(Id);

                    //使用呢query对象对条件和组合进行拼接
                    return criteriaQuery.where(and).orderBy(desc).getRestriction();

  • 相关阅读:
    防火墙应用场景
    如何进行高性能架构的设计
    【机器学习】为什么会产生过拟合,有哪些方法可以预防或克服过拟合?(面试回答)
    企业哪些项目可以参与CMMI的评估?
    深入剖析 Java 类属性与类方法的应用
    CDC一键入湖:当 Apache Hudi DeltaStreamer 遇见 Serverless Spark
    【flask扩展】使用Flask-Mail发送邮件
    windows安装npm教程及生成DEMO
    零基础写框架(3): Serilog.NET 中的日志使用技巧
    机器学习高手之路:发现TensorFlow学习网站的无限可能!
  • 原文地址:https://blog.csdn.net/weixin_53142722/article/details/127417303