SpringBoot分页其实很简单

news2025/2/28 10:32:26

分页其实很简单

一、数据库Limit

Limit的使用

Limit子句可以被用于强制 SELECT 语句返回指定的记录数。

Limit接受一个或两个数字参数,参数必须是一个整数常量。如果给定两个参数,第一个参数指定第一个返回记录行的偏移量,第二个参数指定返回记录行的最大数目。

LIMIT offset,length

//初始记录行的偏移量是 0(而不是 1):
mysql> SELECT * FROM table LIMIT 5,10; //检索记录行6-15

//为了检索从某一个偏移量到记录集的结束所有的记录行,可以指定第二个参数为 -1:
mysql> SELECT * FROM table LIMIT 5,-1; // 检索记录行 6-last

//如果只给定一个参数,它表示返回最大的记录行数目。换句话说,LIMIT n 等价于 LIMIT 0,n:
mysql> SELECT * FROM table LIMIT 5;     //检索前 5 个记录行

Limit的效率

Limit的执行效率高,是对于一种特定条件下来说的:即数据库的数量很大,但是只需要查询一部分数据的情况。原理是:避免全表扫描,提高查询效率

比如:每个用户的phone是唯一的,如果用户使用phone作为用户名登录的话,就需要查询出phone对应的一条记录。

SELECT * FROM t_user WHERE phone=?;
上面的语句实现了查询phone对应的一条用户信息,但是由于phone这一列没有加索引,会导致全表扫描,效率会很低。
SELECT * FROM t_user WHERE email=? LIMIT 1;
加上LIMIT 1,只要找到了对应的一条记录,就不会继续向下扫描了,效率会大大提高。

在一种情况下,使用limit效率低,那就是:只使用limit来查询语句,并且偏移量特别大的情况

做以下实验:
语句1:
  select * from table limit 150000,1000;
  语句2:
  select * from table while id>=150000 limit 1000;
语句1为0.2077秒;语句2为0.0063秒。两条语句的时间比是:语句1/语句2≈33

比较以上的数据时,我们可以发现采用where...limit....性能基本稳定,受偏移量和行数的影响不大,而单纯采用limit的话,受偏移量的影响很大,当偏移量大到一定后性能开始大幅下降。不过在数据量不大的情况下,两者的区别不大。

所以应当先使用where等查询语句,配合limit使用,效率才高

注意,在sql语句中,limt关键字是最后才被处理的,是对查询好的结果进行分页。以下条件的处理顺序一般是:where->group by->having-order by->limit

优化LIMIT

在使用limit之前,先对数据进行一定的处理,比如先用where语句,减少数据的总量之后再分页,或者order by子句用上索引。总之,思路就是优化limit操作对象的检索速度。

二、PageHelper

2.1 PageHelper的使用

依赖引入

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

配置

#分页插件
pagehelper:
  #标识是数据库方言
  helperDialect: mysql
  #启用合理化,如果pageNum<1会查询第一页,如果pageNum>pages会查询最后一页
  reasonable: true
  #为了支持startPage(Object params)方法,增加了该参数来配置参数映射,用于从对象中根据属性名取值, 可以配置pageNum,pageSize,count,pageSizeZero,reasonable,不配置映射的用默认值, 默认值为pageNum=pageNum;pageSize=pageSize;count=countSql;reasonable=reasonable;pageSizeZero=pageSizeZero
  params: count=countSql
  #支持通过 Mapper 接口参数来传递分页参数,默认值false,分页插件会从查询方法的参数值中,自动根据上面 params 配置的字段中取值,查找到合适的值时就会自动分页
  supportMethodsArguments: true
  #如果 pageSize=0 就会查询出全部的结果(相当于没有执行分页查询)
  pageSizeZero: true

使用

// 开始分页
PageHelper.startPage(pageNum, pageSize);
// 查询
List<User> userList = userService.getUserList();
// 封装分页对象
PageInfo<User> pageInfo = new PageInfo<>(userList);

其中,pageNum 表示要查询的页码,pageSize 表示每页的记录数。调用 startPage 方法之后,PageHelper 会自动将下一次查询作为分页查询,并且会在查询之后返回一个 Page 对象,然后可以将这个对象转换为 PageInfo 对象,从而获得分页相关的信息。

2.2 底层原理

  • 首先调用 PageHelperstartPage 方法开启分页,方法中会将分页参数存到一个变量 ThreadLocal<Page> LOCAL_PAGE中;
  • 然后调用 mapper 进行查询,这里实际上会被 PageInterceptor 类拦截,执行其重写的 interceptor 方法,该方法中主要做了以下两件事:
    • 获取到 MappedStatement,拿到业务写好的 sql,将 sql 改造成 select count(0) 并执行查询,并将执行结果存到 LOCAL_PAGE 里的Page 中的 total 属性,表示总条数
    • 获取到 xml 中的 sql 语句,并 append 一些分页 sql 段,然后执行,将执行结果存到 LOCAL_PAGE 里的 Page 中的 list 属性,这里的Page 类实际是 ArrayList 的子类。
  • 结果是封装到了 Page 中,最后交由 PageInfo,从中可以获取到总条数、总页数等参数。

1.分页参数储存

首先看PageHelper.startPage的源码:

public static <E> Page<E> startPage(int pageNum, int pageSize, boolean count, Boolean reasonable, Boolean pageSizeZero) {
    Page<E> page = new Page(pageNum, pageSize, count);
    page.setReasonable(reasonable);
    page.setPageSizeZero(pageSizeZero);
    //获取当前线程中的Page
    Page<E> oldPage = getLocalPage();
    //判断是否存在旧分页数据
    if (oldPage != null && oldPage.isOrderByOnly()) {
        //当只存在orderBy参数时,即为true,也就是说,当存在旧分页数据并且旧分页数据只有排序参数时,就将旧分页数据的排序参数列入新分页数据的排序参数
        page.setOrderBy(oldPage.getOrderBy());
    }
	//将新的分页数据page存入本地线程变量中
    setLocalPage(page);
    return page;
}

其实主要就是把分页参数给到 Page ,然后将实例 Page 存储到 ThreadLocal 中。

public abstract class PageMethod {
    protected static final ThreadLocal<Page> LOCAL_PAGE = new ThreadLocal();

    public PageMethod() {
    }

    protected static void setLocalPage(Page page) {
        LOCAL_PAGE.set(page);
    }
}

2.拦截器改造SQL

1) 统计总数

PageHelper 是通过拦截器底层执行 sql,对应的拦截器是 PageInterceptor,可以看出拦截了 Executor query方法,毕竟 Mybatis 底层查询实际是借助 SqlSeesion 调用 Executor#query

@Intercepts({@Signature(
    type = Executor.class,
    method = "query",
    args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}
), @Signature(
    type = Executor.class,
    method = "query",
    args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class}
)})
public class PageInterceptor implements Interceptor {

看下在哪里进行了总条数查询

在这里插入图片描述
进入 count 方法内看下:
在这里插入图片描述

继续追踪,进入 executeAutoCount方法内
在这里插入图片描述
在这里插入图片描述

继续追踪,进入 getSmartCountSql方法内
在这里插入图片描述

在追踪代码执行过程中,发现进入 executeAutoCount 方法内,这个方法内有个变量为 countSql,其内容正是select count(0)...,说明 PageHelper 在此处进行了总条数查询。

2) 分页查询

再看下 intercept 方法中如何进行如何分页查询:
在这里插入图片描述

pageQuery 方法中进行实际查询操作:
在这里插入图片描述

方法中的 pageSql 即为分页查询语句,看下 getPageSql 是如何实现的:
在这里插入图片描述
在这里插入图片描述
很明显看出 PageHelper 在分页查询时对每一个查询 sql 末尾都增加了 limit 子句。

值得注意的是,在 intercept 方法末尾的 finally 中调用 afferAll 方法对 ThreadLocal 进行 remove。

public void afterAll() {
    AbstractHelperDialect delegate = this.autoDialect.getDelegate();
    if (delegate != null) {
        delegate.afterAll();
        this.autoDialect.clearDelegate();
    }

    clearPage();
}

public static void clearPage() {
    LOCAL_PAGE.remove();
}
3) PageInfo

实际代码中进行分页查询得到list 之后,还要将其封装进 PageInfo 类中,才能获取到分页信息。我们关注下 PageInfo 中的构造器:

在这里插入图片描述
在这里插入图片描述

在这段代码中,将 list强转为 PagePage 类实际上是 ArrayList 的子类,且 Page 类中包含了分页的具体信息,而分页查询返回的 list 实际类型就是 Page,所以将其封装为 PageInfo 再返回

2.3 安全问题

PageHelperstartPage 方法使用了静态的 ThreadLocal 参数,分页参数和线程是绑定的。 只要保证在 startPage 方法调用后紧跟 MyBatis 查询方法,这就是安全的。因为 PageHelperfinally 代码段中自动清除了 ThreadLocal存储的对象。但是例如下面这样的代码,就是不安全的用法:

PageHelper.startPage(1, 10);
List<User> list;
if (param1 != null) {
    list = userMapper.selectIf(param1);
} else {
    list = new ArrayList<User>();
}

这种情况下由于 param1 存在 null 的情况,就会导致 PageHelper 生产了一个分页参数,但是没有被消费,这个参数就会一直保留在这个线程上。当这个线程再次被使用时,就可能导致不该分页的方法去消费这个分页参数,这就产生了莫名其妙的分页。正确写法如下:

List<User> list;
if (param1 != null) {
    PageHelper.startPage(1, 10);
    list = userMapper.selectIf(param1);
} else {
    list = new ArrayList<User>();
}

三、PageHelper什么场合应该用

由于pageHelper拦截我们写的sql语句,自己重新包装一层,在后面添加limit,这在数据量小的时候,比在每个mapper.xml中自己添加要方便很多

注意:一旦数据量过大,分页时limit的偏移量必然增大 ,不可避免的,查询的时间就会呈几何倍数增长。此时应该用方法一中Limit的优化写法

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

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

相关文章

Java————初始集合框架

一 、 集合框架 Java 集合框架Java Collection Framework &#xff0c;又被称为容器container &#xff0c; 是定义在 java.util 包下的一组接口interfaces 和其实现类classes 。 其主要表现为将多个元素element 置于一个单元中&#xff0c; 用于对这些元素进行快速、便捷的存…

题目 1056: 二级C语言-温度转换

输入一个华氏温度&#xff0c;要求输出摄氏温度。公式为 保留两位小数 样例输入 -40.00 样例输出 -40.00 这道题很简单&#xff0c;数据代入公式就行。记得设置double或者float的浮点型--》用于保留两位小数。 对于保留小数: 1是可以用iomanip的cout<<fixed<&l…

Mysql的逻辑架构、存储引擎

1. 逻辑架构剖析 1.1 服务器处理客户端请求 首先MySQL是典型的C/S架构&#xff0c;即Clinet/Server 架构&#xff0c;服务端程序使用的mysqld。 不论客户端进程和服务器进程是采用哪种方式进行通信&#xff0c;最后实现的效果是&#xff1a;客户端进程向服务器进程发送一段文…

MySQL数据库详解 一:安装MySQL数据库及基本管理

文章目录 1. 数据库的基本概念1.1 数据库的组成1.1.1 数据1.1.2 表1.1.3 数据库 1.2 当前主流数据库及其特点1.3 数据库类型1.3.1 关系数据库1.3.1.1 关系数据库的组成1.3.1.2 非关系数据库 2. 安装MySQL2.1 yum安装2.2 编译安装MySQL2.2.1 前置准备2.2.2 编译安装2.2.3 修改my…

《C和指针》笔记27:递归

递归所需要的两个特性&#xff1a; 存在限制条件&#xff0c;当符合这个条件时递归便不再继续&#xff1b;每次递归调用之后越来越接近这个限制条件。 这里没有用计算阶乘和菲波那契数列的例子说明递归&#xff0c;作者指出前者递归并没有提供任何优越之处。而后者效率之低是…

渗透测试的概况、依据、内容方法和流程有哪些?

一、项目概况 通过模拟黑客的思维和攻击手段&#xff0c;对计算机业务系统的弱点、技术缺陷和漏洞进行探查评估。经过客户授权后&#xff0c;在不影响业务系统正常运行的条件下&#xff0c;渗透人员在黑客可能的不同的位置&#xff0c;采取可控的方法、手段和工具&#xff0c;…

Java计算机毕业设计 基于SpringBoot+Vue的毕业生信息招聘平台的设计与实现 Java实战项目 附源码+文档+视频讲解

博主介绍&#xff1a;✌从事软件开发10年之余&#xff0c;专注于Java技术领域、Python人工智能及数据挖掘、小程序项目开发和Android项目开发等。CSDN、掘金、华为云、InfoQ、阿里云等平台优质作者✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精…

Input子系统 - Kernel驱动程序 - Android

Input子系统 - Kernel驱动程序 - Android 1、Input子系统相关定义1.1 代码位置1.2 input_dev结构体&#xff1a;表示输入设备1.3 input_handler结构体&#xff1a;struct input_handler - implements one of interfaces for input devices1.4 input_handle结构体&#xff1a;将…

图文文案音视频素材库流量主小程序开发

适用于全行业的资源素材运营变现小程序&#xff0c;支持文档、图片、文件、图文、音视频、网盘等多种资源形式&#xff0c;多种功能组合运营变现的小程序。 适用领域&#xff1a; 公司/微商素材、学习/考研/论文资料分享、PPT模板/背景图/壁纸/头像、知识付费、抖音素材等等…

传统 IAM 已成为企业增长桎梏,下一代身份基础设施如何帮助企业破局?

近期&#xff0c;国际权威研究机构 Gartner 发布了《Hype Cycle for Data, Analytics and AI in China, 2023》&#xff08;2023 中国数据、分析与 AI 技术成熟度曲线报告&#xff09;。报告指出&#xff0c;数据、分析技术和人工智能对中国的数字经济和国家战略至关重要&#…

经典匹配算法: KMP、Sunday与ShiftAnd

本次介绍的三种算法的时间复杂度&#xff1a; 基础概念&#xff1a; 图3 图1 单模匹配问题&#xff1a;单个模式串&#xff0c;比如我们要在一个长串&#xff08;母串S&#xff09;中查找一个短串&#xff08;模式串T&#xff09;是否出现过。 暴力匹配算法&#xff1a; 算法…

IDEA下使用Spring MVC

<?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation"http://maven.apache.org/POM/4.0.0 http://ma…

56资源网系统源码搭建知识付费-含源码

内置了上万条数据资源 大致功能&#xff1a; 支持免费与付费&#xff08;增加了插件付费插件&#xff09;支持侧边栏支持添加各类型广告&#xff08;你所能用到的基本都有&#xff09;.支持网盘下载模块支持所有页面自定义支持文章页三方跳转支持添加页面支持自定义采集&#…

nginx配置指南

nginx.conf配置 找到Nginx的安装目录下的nginx.conf文件&#xff0c;该文件负责Nginx的基础功能配置。 配置文件概述 Nginx的主配置文件(conf/nginx.conf)按以下结构组织&#xff1a; 配置块功能描述全局块与Nginx运行相关的全局设置events块与网络连接有关的设置http块代理…

Python Opencv实践 - 视频文件写入(格式和分辨率修改)

参考资料&#xff1a; python opencv写视频——cv2.VideoWriter()_cv2.cv.videowriter(_翟羽嚄的博客-CSDN博客 import cv2 as cv import numpy as np#1. 打开原始视频 video_in cv.VideoCapture("../SampleVideos/Unity2D.mp4") video_width int(video_in.get(c…

优化器的使用

代码示例&#xff1a; import torch import torchvision from torch import nn from torch.nn import Conv2d, MaxPool2d, Flatten, Linear, Sequential from torch.utils.data import DataLoader from torch.utils.tensorboard import SummaryWriter# 加载数据集转化为Tensor…

腾讯mini项目-【指标监控服务重构】2023-08-22

今日已办 50字项目价值和重难点 项目价值 通过将指标监控组件接入项目&#xff0c;对比包括其配套工具在功能、性能上的差异、优劣&#xff0c;给出监控服务瘦身的建议 top3难点 减少监控服务资源成本&#xff0c;考虑性能优化如何证明我们在监控服务差异、优劣方面的断言…

ubuntu 22.04运行opencv4的c++程序遇到的问题

摘要&#xff1a;本文介绍一下在ubuntu系统中&#xff0c;运行一个最简单的opencv4程序都出问题的解决方法&#xff0c;并对其基本原理作简单阐述。解决问题的方法有很多&#xff0c;本文只提供其中一种。 opencv版本是4.2.0&#xff0c;ubuntu版本是20.04 查询opencv版本的指…

Aztec.nr:Aztec的隐私智能合约框架——用Noir扩展智能合约功能

1. 引言 前序博客有&#xff1a; Aztec的隐私抽象&#xff1a;在尊重EVM合约开发习惯的情况下实现智能合约隐私 Aztec.nr&#xff0c;为&#xff1a; 面向Aztec应用的&#xff0c;新的&#xff0c;强大的智能合约框架使得开发者可直观管理私有状态基于Noir构建&#xff0c;…

写一篇nginx配置指南

nginx.conf配置 找到Nginx的安装目录下的nginx.conf文件&#xff0c;该文件负责Nginx的基础功能配置。 配置文件概述 Nginx的主配置文件(conf/nginx.conf)按以下结构组织&#xff1a; 配置块功能描述全局块与Nginx运行相关的全局设置events块与网络连接有关的设置http块代理…