Java开发 - 分页查询初体验

news2024/11/18 7:43:36

前言

在上一篇,我们对es进行了深入讲解,相信看过的小伙伴已经能基本掌握es的使用方法,我们知道,es主要针对的是搜索条件,在这方面es具有无可比拟的优势,但我们也会注意到,有时候搜索条件过于宽泛的时候,搜索结果集也将是非常庞大的,不仅服务器接口压力大,用户等的也很为难,为了解决这一问题,必须要对返回的数据进行处理,此时,分页就出现了,接下来,博主讲带两大家了解查询时的分页问题,并解决这个问题。

什么是分页查询

如上所述,分页查询是在返回数据量比较庞大时,为了能提高用户的体验,减少等待时间,而产生的一种数据分段方式,体现在展示层上就是数据的上拉加载,我们经常可以在购物类app上见到这样的设定,大多数情况下我们一页数据都为10条,大概是10条基本能满足查看一屏的需求和减少用户的等待时间的缘故,这是在移动端,在pc上可能略有不同,但基本都会以10的倍数存在。这便是分页查询。

分页查询的优点

分页查询的有点其实在上文中都已经说明,此处做一个总结,主要体现在三点上:

服务器

一次性查询所有信息,服务器压力过大,分页查询则可以降低服务器压力。

客户端

一次性显示所有信息,需要更多流量,加载更久,分页显示则可以解决这个问题。

用户体验

良好的用户体验可以让用户在应用上花费更多的时间,才能够提高购买率,也是服务商所希望的。最重要的一点,用户才不会放弃当前使用的应用。而且一般查询的数据都是会排序的,所以有价值的数据都会在前几页。

es的分页查询

前文es中没有对分页查询做说明,我们接上一篇的结尾,在项目中继续操作,但在说分页前,我们需要先对排序做一个了解,es的排序写法其实和SQL很相似,这在前文中我们已经有所见识,下面就一起来看看吧。

排序

排序和上一篇结尾的单条件/多条件查询都属于条件查询,我们在Repository类中来写一下怎么进行排序,在类中添加如下方法:

    // 排序查询
// 默认情况下,ES查询结果按score排序,如果想按其他的规则排序可以加OrderBy
// 和数据库一样,默认升序排序 Desc结尾会降序
    Iterable<People> queryItemsByNameMatchesOrderByBraveDesc(String name, Double brave);

接着进行测试,在测试类中添加如下测试方法:

    // 排序查询
    @Test
    void queryOrder(){
        Iterable<People> items=peopleRepository
                .queryItemsByNameMatchesOrderByBraveDesc("西游");
        items.forEach(item -> System.out.println(item));
    }

提示:在测试前,务必保证es处于启动状态,由于不是当天写的,所以es停止了,运行后发现报错,启动es后正常,下面看运行结果:

是按照我们想要的战力进行的排序,测试成功,你成功了吗?

来看看底层的代码逻辑:


### 单字段搜索排序
POST http://localhost:9200/peoples/_search
Content-Type: application/json

{
  "query": {"match": { "name":  "西游" }},
  "sort":[{"brave":"desc"}]
}

可以在条件中增加其他的条件进行尝试。

分页加排序

我们在使用es时用了SpringData框架,其支持分页查询,只需要修改参数和返回值就能实现自动分页的效果,这就帮我们省去了很多的代码和功夫,下面让我们来看看它是怎么分页的。

我们以刚刚的排序为例,在其基础上进行修改,额外增加一个方法:

    // 分页查询
// 当查询数据较多时,我们可以利用SpringData的分页功能,按用户要求的页码查询需要的数据
// 返回值修改为Page类型,这个类型对象除了包含Iterable能够包含的集合信息之外,还包含分页信息
    Page<People> queryItemsByNameMatchesOrderByBraveDesc(String name, Pageable pageable);

接着我们开始进行测试,由于我们数据量比较少,我们会把每页的数据设置的很少,看看代码怎么写:

// 分页查询
    @Test
    void queryPage(){
        int pageNum=1;  //页码
        int pageSize=2; //每页条数
        Page<People> page= peopleRepository
                .queryItemsByNameMatchesOrderByBraveDesc(
                        "西游", PageRequest.of(pageNum-1,pageSize));
        page.forEach(item -> System.out.println(item));
        // page对象中还包含了一些基本的分页信息
        System.out.println("总页数:"+page.getTotalPages());
        System.out.println("当前页:"+page.getNumber());
        System.out.println("每页条数:"+page.getSize());
        System.out.println("当前页是不是首页:"+page.isFirst());
        System.out.println("当前页是不是末页:"+page.isLast());
    }

我们在这里要注意分页的起始页,程序中我们第一位总是0开始的,但在生活中都是从1开始的,为了让主观上认为从1开始,我们在内部做减1操作。

运行测试代码,查看结果:

结果是按照排序后进行分页的,数据完全正确,测试成功。es的分页到这里就结束了,你学会了吗?新来的童鞋建议先看前一篇es,结合代码操作一遍,理解会更加的透彻。

数据库分页

PageHelper

SQL中我们通过limit关键字来进行分页查询,但却需要实时进行计算,在Spring Data中,我们通过框架帮我们解决了这个问题,PageHelper也是一个框架,它帮我们自动实现了分页效果,我们可以像Spring Data里那样,通过提供页码和数量来达到分页的目的。

所谓自动实现,也只是PageHelper在程序运行时,通过向SQL语句末尾添加limit的方式来实现分页,不要觉得讽刺,你当然可以选择自己来写,那是你的自由,我们使用框架的目的就是帮助我们降低开发的成本,提高开发的效率。

为了说明PageHelper的用法,我们还是在微服务的项目中进行同步讲解,以代码实战的形式进行。没有看过微服务篇的同学可以先去看看,这里要用到项目的工程,你也可以选择自建一个新的工程。但可能会略微有些麻烦。

添加依赖

我们在order模块进行代码的编写,首先添加依赖如下:

<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
</dependency>

此依赖在添加senta依赖时已经添加过,我们要知道。

基本使用

持久层

我们在OrderMapper接口中添加查询的方法如下:

    @Select("select id,user_id,commodity_code,count,money from order_tbl")
    List<Order> findAllOrders();

需要注意,此方法不需要添加分页相关的参数,如limit,我们使用的框架会自动追加参数进去。他不区分SQL语句是写在注解中还是xml文件中。

业务逻辑层

在实现类OrderServiceImpl中添加此方法:

    // 分页查询所有订单的方法
// pageNum是要查询的页码
// pageSize是每页的条数
    public PageInfo<Order> getAllOrdersByPage(Integer pageNum, Integer pageSize){

        // 利用PageHelper框架的功能,指定分页的查询的页码和每页条数
        // pageNum为1时,就是查询第一页,和SpringData的分页不同(SpringData分页0表示第一页)
        PageHelper.startPage(pageNum,pageSize);
        // 调用查询所有订单的方法
        // 因为上面设置了分页查询的条件,所以下面的查询就会自动在sql语句后添加limit关键字
        // 查询出的list就是需要查询的页码的数据
        List<Order> list=orderMapper.findAllOrders();
        // 我们完成了分页数据的查询,但是当前方法要求返回分页信息对象PageInfo
        // PageInfo中可以包含分页数据和各种分页信息,这些信息都是自定计算出来的
        // 要想获得这个对象,可以在执行分页查询后实例化PageInfo对象,所有分页信息会自动生成
        return new PageInfo<>(list);
    }

PageInfo对象既包含查询数据结果,又包含分页信息,我们来看看该类中有哪些属性:

//当前页
private int pageNum;
//每页的数量
private int pageSize;
//当前页的行数量
private int size;
//当前页面第一个元素在数据库中的行号
private int startRow;
//当前页面最后一个元素在数据库中的行号
private int endRow;
//总页数
private int pages;
//前一页页号
private int prePage;
//下一页页号
private int nextPage;
//是否为第一页
private boolean isFirstPage;
//是否为最后一页
private boolean isLastPage;
//是否有前一页
private boolean hasPreviousPage;
//是否有下一页
private boolean hasNextPage;
//导航条中页码个数
private int navigatePages;
//所有导航条中显示的页号
private int[] navigatepageNums;
//导航条上的第一页页号
private int navigateFirstPage;
//导航条上的最后一页号
private int navigateLastPage;

根据需要选择需要的参数。

控制器层

业务逻辑层完成后,就需要在控制器层通过接口来调用接口方法,在OrderController类中添加如下控制器方法:

    @GetMapping("/page")
    @ApiOperation("分页查询订单")
    @ApiImplicitParams({
            @ApiImplicitParam(value = "页码",name="pageNum",example = "1"),
            @ApiImplicitParam(value = "每页条数",name="pageSize",example = "10")
    })
    public JsonResult<PageInfo<Order>> pageOrders(Integer pageNum, Integer pageSize){
        // 分页调用
        PageInfo<Order> pageInfo=orderService.getAllOrdersByPage(pageNum,pageSize);
        return JsonResult.ok("查询完成",pageInfo);
    }

你可以从方法中看到我们之前写的方法的影子,只是在参数方面我们做了一些调整,加入了分页相关的信息。由于我们这里没有写接口方法,所以为了调用实现类中的方法,我们需要修改此类中IOrderService为OrderServiceImpl,直接调用实现方法。

测试

在这里,启动nacos和seata,如果es还开启着,可以关闭了,博主的电脑都开始烫了。接着运行order

模块,额,报错?看看啥报错:

java.sql.SQLNonTransientConnectionException: Public Key Retrieval is not all

以前都没报这个错啊,奇怪了,数据库也没升级,经查询,通过在spring.datasource.url后添加allowPublicKeyRetrieval=true可解决此问题。注意添加的格式:

    url: jdbc:mysql://localhost:3306/cloud_db?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai&allowMultiQueries=true&allowPublicKeyRetrieval=true

启动成功后,我们在浏览器输入在线文档的地址:http://localhost:20002/doc.html

打开此页面:

点击发送按钮,可看到如下数据:

博主的数据库有11条数据,第一页给了10条数据,将页码改为2后,点击发送:

控制台中也打印出了自动追加limit的SQL语句:

==> Preparing: select id,user_id,commodity_code,count,money from order_tbl LIMIT ?, ?
==> Parameters: 10(Long), 10(Integer)

这里只返回了一条数据,就代表我们的测试已经成功了。但,我们注意到,上面的代码中还是存在一定问题,因为我们没有使用接口进行调用,而是直接调用了实现类方法,这是不符合规则的。原因可以看下方数据返回的内容。下面,我们将来完善这个步骤。

数据返回

分页查询我们在上面的方法中使用的PageInfo作为返回值,这是不合适的,因为使用此类,这个类就会出现在任何调用这个方法的模块,这些模块都将添加PageHelper的依赖,而且我们也不方便来做一些自定义的东西,为了解决此问题,我们需要在commons模块中添加一个返回类来代替PageInfo。

添加依赖

所以在PageInfo模块中,我们添加依赖如下:

<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.2.0</version>
</dependency>

添加替代类

接着我们在restful包中新建一个JsonPage类,将PageInfo类封装进去:

package com.codingfire.cloud.commons.restful;

import com.github.pagehelper.PageInfo;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

import java.io.Serializable;
import java.util.List;

// 通用支持分页查询的结果对象类型
@Data
public class JsonPage<T> implements Serializable {

    // 按照实际需求,定义这个类中的属性
    @ApiModelProperty(value = "当前页码",name = "pageNum")
    private Integer pageNum;
    @ApiModelProperty(value = "每页条数",name = "pageSize")
    private Integer pageSize;
    @ApiModelProperty(value = "总条数",name = "totalCount")
    private Long totalCount;
    @ApiModelProperty(value = "总页数",name = "totalPages")
    private Integer totalPages;
    // 声明一个属性,来承载查询到的分页数据结果
    @ApiModelProperty(value = "分页数据",name = "list")
    private List<T> list;

    // 所有属性写完了,下面要编写将其他框架的分页结果转换成当前类对象的方法
    // SpringDataElasticsearch或PageHelper等具有分页功能的框架,均有类似PageInfo的对象
    // 我们可以分别编写方法,将它们转换成JsonPage对象,我们先只编写PageHelper的转换
    public static <T> JsonPage<T> restPage(PageInfo<T> pageInfo){
        // 下面开始将pageInfo对象的属性赋值给JsonPage对象
        JsonPage<T> result=new JsonPage<>();
        result.setPageNum(pageInfo.getPageNum());
        result.setPageSize(pageInfo.getPageSize());
        result.setTotalCount(pageInfo.getTotal());
        result.setTotalPages(pageInfo.getPages());
        result.setList(pageInfo.getList());
        // 返回赋值完毕的JsonPage对象
        return result;
    }


}

修改业务逻辑层

IOrderService接口类中添加新接口方法:

    // 分页查询所有订单的方法
    JsonPage<Order> getAllOrdersByPage(Integer pageNum, Integer pageSize);

接着在实现类OrderServiceImpl中修改原来的实现方法如下:

public JsonPage<Order> getAllOrdersByPage(Integer pageNum, Integer pageSize){

    // 利用PageHelper框架的功能,指定分页的查询的页码和每页条数
    // pageNum为1时,就是查询第一页,和SpringData的分页不同(SpringData分页0表示第一页)
    PageHelper.startPage(pageNum,pageSize);
    // 调用查询所有订单的方法
    // 因为上面设置了分页查询的条件,所以下面的查询就会自动在sql语句后添加limit关键字
    // 查询出的list就是需要查询的页码的数据
    List<Order> list=orderMapper.findAllOrders();
    // 我们完成了分页数据的查询,但是当前方法要求返回分页信息对象PageInfo
    // PageInfo中可以包含分页数据和各种分页信息,这些信息都是自定计算出来的
    // 要想获得这个对象,可以在执行分页查询后实例化PageInfo对象,所有分页信息会自动生成
    //     ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
    return JsonPage.restPage(new PageInfo<>(list));
}

修改控制器层

此时,控制器层因为上面的代码修改已经报错了,然后修改控制器层方法,有两处:

第一处是OrderServiceImpl:

    private OrderServiceImpl orderService;
    private IOrderService orderService;

其实就是修改回来,第一行改回第二行。

第二处是原方法修改返回值:

    public JsonResult<JsonPage<Order>> pageOrders(Integer pageNum, Integer pageSize){
        // 分页调用
        JsonPage<Order> jsonPage=orderService.getAllOrdersByPage(pageNum,pageSize);
        return JsonResult.ok("查询完成",jsonPage);
    }

一路修改,到这里就结束了。我们可以重新order模块,通过在线文档来进行测试,发现测试结果是一样的,这就可以了,测试成功,这一步是对返回值做了处理,使我们可以自定义返回值的对象,是代码更加的灵活,你可以对比修改前后返回值的结构。

结语

分页查询在开发中的使用非常频繁,就是再小的项目也离不开分页功能,虽然不一定使用es,但分页绝对会用,学完此篇,分页功能你就基本掌握了,剩下的就是在实践中使用此功能,相信你一定已经学会了。另外,分页功能还不算难,和es比起来算是比较基础的功能了,只是涉及es的分页,才展开讲了一下,这是每一个后台开发者都必须会的内容,大家有时间多做练习,自己进行尝试,才能够掌握得更好。又到了和大家说再见的时候,觉得不错,就三连(点赞,收藏,评论)支持一下吧。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/380747.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于JavaWeb实现的学生选课系统(源码+数据库+文档)

一、项目简介 本项目是一套基于JavaWeb实现的学生选课系统&#xff0c;主要针对计算机相关专业的正在做毕设的学生与需要项目实战练习的Java学习者。 包含&#xff1a;项目源码、数据库脚本等&#xff0c;该项目附带全部源码可作为毕设使用。 项目都经过严格调试&#xff0c;e…

基于开源IM即时通讯框架MobileIMSDK:RainbowChat-iOS端v6.2版已发布

关于MobileIMSDK MobileIMSDK 是一套专门为移动端开发的开源IM即时通讯框架&#xff0c;超轻量级、高度提炼&#xff0c;一套API优雅支持UDP 、TCP 、WebSocket 三种协议&#xff0c;支持iOS、Android、H5、标准Java平台&#xff0c;服务端基于Netty编写。 工程开源地址是&…

Nuxt.js项目开发过程遇到的问题以及对Nuxt.js的学习与总结

文章目录&#x1f4cb;前言&#x1f4bb;Nuxtjs3快速了解&#x1f3af;nuxtjs是什么&#xff1f;官网是这样介绍它的。&#x1f3af;关于nuxtjs的SSR开发&#x1f9e9;SSR应用场景&#x1f9e9;nuxtjs的特性&#x1f4bb;nuxtjs的初始目录结构&#x1f3af;关于各个目录的解释&…

智能家居控制系统

&#x1f941;作者&#xff1a; 华丞臧. &#x1f4d5;​​​​专栏&#xff1a;【项目经验】 各位读者老爷如果觉得博主写的不错&#xff0c;请诸位多多支持(点赞收藏关注)。如果有错误的地方&#xff0c;欢迎在评论区指出。 推荐一款刷题网站 &#x1f449; LeetCode刷题网站…

MIGO 物料过账 创建物料凭证 BAPI_GOODSMVT_CREATE

文章目录1.前台操作2.需求分析2.1调用方式2.2分为两大概括:2.3业务逻辑细节图3.BAPI_GOODSMVT_CREATE4.RFC接口代码5.总结1.前台操作 SAP CO01(创建生产订单)/MIGO(发货投料)前台操作 这里面有migo的前台操作,首先了解前台操作后再去写RFC接口是比较容易理解的.!! 2.需求分析…

opencv读入图片注意事项

来源&#xff1a;投稿 作者&#xff1a;蓬蓬奇 编辑&#xff1a;学姐 深度学习数据预处理中常用opencv读入图片&#xff0c;一般在__getitem__函数中调用。本文主要介绍opencv读取图片的一些细节以及注意事项。本文中使用的图片见第6节“opencv测试使用的图片”。 1.如何通过o…

计算机底层:储存器的性能指标(CPU和内存等硬件的性能以及 对比标准)

计算机底层&#xff1a;储存器的性能指标(CPU和内存等硬件的性能以及 对比标准) 内存&#xff1a; MAR是存放地址的寄存器&#xff1b;MDR是存放数据的寄存器。 MAR是存放地址的寄存器&#xff0c;那么其中的二进制位一定是不能重复的&#xff0c;试想&#xff0c;如果有有两个…

《爆肝整理》保姆级系列教程python接口自动化测试框架(二十六)--批量执行用例 discover(详解)

简介  我们在写用例的时候&#xff0c;单个脚本的用例好执行&#xff0c;那么多个脚本的时候&#xff0c;如何批量执行呢&#xff1f;这时候就需要用到 unittest 里面的 discover 方法来加载用例了。加载用例后&#xff0c;用 unittest 里面的 TextTestRunner 这里类的 run 方…

类和对象 - 上

本文已收录至《C语言》专栏&#xff01; 作者&#xff1a;ARMCSKGT 目录 前言 正文 面向过程与面向对象 面向过程的解决方法 面向对象的解决方法 面向对象的优势 类的引入 早期C类的实现 class定义类 class定义规则 类成员的两种定义方式 类的访问限定符及封装 访…

在数字优先的世界中打击知识产权盗窃

在当今数据驱动的世界中&#xff0c;全球许多组织所面临的期望和需求正在达到前所未有的水平。 为了迎接挑战&#xff0c;数据驱动的方法是必要的&#xff0c;需要有效的数字化转型来提高运营效率、简化流程并从遗留技术中获得更多收益。 但是&#xff0c;虽然数字优先方法可…

Spring Boot+Vue前后端分离项目练习01之网盘项目的搭建

1.前期设计 一般看来&#xff0c;网盘系统主要实体包括&#xff1a;用户、存储信息、用户文件、文件、回收文件等基本功能点。 各实体对应的表结构如下所示&#xff1a; 表名&#xff1a;user&#xff08;用户表&#xff09; 字段名属性说明userIdbigint主键usernamevarcha…

数据结构与算法系列之顺序表的实现

这里写目录标题顺序表的优缺点&#xff1a;注意事项test.c&#xff08;动态顺序表&#xff09;SeqList.hSeqList.c各接口函数功能详解void SLInit(SL* ps);//定义void SLDestory(SL* ps);void SLPrint(SL* ps);void SLPushBack(SL* ps ,SLDataType * x );void SLPopBack(SL* ps…

[busybox] busybox生成一个最精简rootfs(下)

书接上回&#xff1a;[busybox] busybox生成一个最精简rootfs(上) 本篇介绍几个rootfs中用到的“不是那么重要的”几个文件。 9 /etc/shadow 和 /etc/passwd 曾经&#xff0c;/etc/passwd 文件用于存储独立 Linux 系统中的所有登录信息。 后来&#xff0c;由于以下原因&…

算法训练营 day63 单调栈 下一个更大元素II 接雨水

算法训练营 day63 单调栈 下一个更大元素II 接雨水 下一个更大元素II 503. 下一个更大元素 II - 力扣&#xff08;LeetCode&#xff09; 给定一个循环数组 nums &#xff08; nums[nums.length - 1] 的下一个元素是 nums[0] &#xff09;&#xff0c;返回 nums 中每个元素的…

2023软件测试工程师涨薪攻略,3年如何达到30K

1.软件测试如何实现涨薪 首先涨薪并不是从8000涨到9000这种涨薪&#xff0c;而是从8000涨到15K加到25K的涨薪。基本上三年之内就可以实现。 如果我们只是普通的有应届毕业生或者是普通本科那我们就只能从小公司开始慢慢往上走。 有些同学想去做测试&#xff0c;是希望能够日…

Vue3 关于 provide、inject 的用法

前言&#xff1a; 在前端项目中牵扯的最多的莫过于组件之间的传值了&#xff0c;除了最最常用的 props 和 emit&#xff0c;其实在 Vue 中还额外提供了另外几种方法。今天分享一种组件之间通信的方法。 一. 场景再现 先别着急考虑标题这个 api 的含义。在这里我先动手写一个比较…

如何使用Codecepticon对C#、VBA宏和PowerShell源代码进行混淆处理

关于Codecepticon Codecepticon是一款功能强大的代码混淆处理工具&#xff0c;该工具专为红队和紫队渗透测试安全活动而开发&#xff0c;在该工具的帮助下&#xff0c;广大研究人员可以轻松对C#、VBA5/VBA6&#xff08;宏&#xff09;和PowerShell源代码进行混淆处理。Codecep…

【Datawhale图机器学习】图神经网络

图神经网络 GNN是一种连接模型&#xff0c;通过网络中节点之间的信息传递的方式来获取图中的依存关系&#xff0c;GNN通过从节点任意深度的邻居来更新该节点状态&#xff0c;这个状态能够表示状态信息。第一次在论文 The graph neural network model 中提出 与传统NN的区别&a…

UnsupportedOperationException深层探究

在调试程序的时候发现一个奇怪的问题&#xff0c;代码都是写过的很常见的代码&#xff0c;但是看日志会报错&#xff0c;报错信息如下&#xff1a;Exception in thread "main" java.lang.UnsupportedOperationExceptionat java.util.AbstractList.set(AbstractList.j…

mysql last lesson

1:创建用户 create user zhang identified by 12345678;2&#xff1a;给用户授权&#xff0c;撤销授权&#xff0c; grant.......to revoke ....... 3:将数据库中的数据导出 C:\Windows\system32>mysqldump bjpowernode>C:\bjpowernode.sql -uroot -p12345678 4&#…