当前位置: 代码网 > it编程>编程语言>Java > Java分页查询实现方式

Java分页查询实现方式

2026年09月03日 Java 我要评论
分页查询核心思路分页的本质是限制查询结果的条数 + 跳过指定行数,并查询总记录数(用于计算总页数)。核心参数:pagenum:当前页码(从 1 开始)pagesize:每页显示条数起始行计算:star

分页查询核心思路

分页的本质是限制查询结果的条数 + 跳过指定行数,并查询总记录数(用于计算总页数)。

核心参数:

  • pagenum:当前页码(从 1 开始)
  • pagesize:每页显示条数
  • 起始行计算:startrow = (pagenum - 1) * pagesize
  • 总页数计算:totalpages = (totalcount + pagesize - 1) / pagesize(向上取整)

常见分页实现方式

mybatis

1、mapper手动拼sql

mapper 接口:

import org.apache.ibatis.annotations.param;
import java.util.list;

public interface usermapper {
    // 查询分页数据
    list<user> selectuserbypage(@param("startrow") int startrow, @param("pagesize") int pagesize);
    // 查询总条数
    int selectusertotalcount();
}

mapper.xml:

<?xml version="1.0" encoding="utf-8"?>
<!doctype mapper public "-//mybatis.org//dtd mapper 3.0//en" 
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.mapper.usermapper">
    <!-- 分页查询数据 -->
    <select id="selectuserbypage" resulttype="com.example.entity.user">
        select id, name, age from user limit #{startrow}, #{pagesize}
    </select>
    <!-- 查询总条数 -->
    <select id="selectusertotalcount" resulttype="int">
        select count(*) from user
    </select>
</mapper>

2、pagehelper 插件

需要在 pom.xml 中添加 pagehelper 依赖

<dependency>
    <groupid>com.github.pagehelper</groupid>
    <artifactid>pagehelper-spring-boot-starter</artifactid>
    <version>1.4.7</version>
</dependency>

在调用时添加如下代码

// 开启分页(只对紧接着的第一个查询生效)
pagehelper.startpage(pagenum, pagesize);

mybatis-plus

1、使用内置分页插件 paginationinnerinterceptor

page<forlan> page = new page<>(pagenum, pagesize);
querywrapper<forlan> querywrapper = new querywrapper<>();
querywrapper.eq("id", id);
mapper.selectpage(page, querywrapper);

service.lambdaquery().page(new page<>(pagenum, pagesize))

2、基于偏移量的分页(升级写法,自己控制结束)

核心参数:

1.limit n

  • 表示“只返回最多 n 条记录”。
  • 通常用来控制每页显示的数据条数。

2.offset m

  • 表示“跳过前 m 条记录”,从第 m+1 条开始取数据。
  • 用于定位到当前请求的页码起始位置。

核心:service.query().last("limit " + batchsize + " offset " + offset).list();

final int batchsize = 1000;
list<forlan> allresults = new arraylist<>();
int offset = 0;
boolean hasmoreresults = true;
while (hasmoreresults) {
	try {
		list<forlan> batchresults = service.query().last("limit " + batchsize + " offset " + offset).list();
		if (batchresults == null || batchresults.isempty()) {
			hasmoreresults = false;
		} else {
			allresults.addall(batchresults);
			offset += batchresults.size();
		}
	} catch (exception e) {
		log.info("error querying batch: ", e);
		hasmoreresults = false;
	}
}
return allresults;

上面的写法,存在问题:随着 offset 增大,性能下降严重,对大数据量场景不友好,适合深度翻页,比如:

-- 查询第10001页,每页10条数据
select * from products order by id limit 10 offset 100000;

这条sql的执行逻辑并非直接定位到第100,001条记录。mysql的实际处理过程是:从存储引擎中读取满足条件的前 100010 (offset + limit) 条记录,在服务层(server layer)对这些记录进行排序,抛弃前面的 100000 条记录,返回最终的 10 条记录。

所以,offset 值越大,mysql需要扫描、加载并最终抛弃的行数就越多,这导致了巨大的i/o和cpu资源浪费,是性能下降的直接原因。

1)延迟关联:优化后的写法

核心思想:先通过覆盖索引快速定位到目标页的主键id,然后再关联原表获取完整的行数据,从而减少对主表数据的扫描。

long lastid = 0l;
final int batchsize = 1000;
list<forlan> allresults = new arraylist<>();
boolean hasmoreresults = true;

while (hasmoreresults) {
	try {
	    list<forlan> batchresults = service.query().gt("id", lastid).last("limit " + batchsize).list();
		if (batchresults == null || batchresults.isempty()) {
			hasmoreresults = false;
		} else {
			allresults.addall(batchresults);
			lastid = batchresults.get(batchresults.size() - 1).getid();
		}
	} catch (exception e) {
		log.info("error querying batch: ", e);
		hasmoreresults = false;
	}
}
return allresults;

存在问题,如果扫描的最小id在几千万,这时候首次查询也是非常耗费时间的,进一步优化的写法如下:

final int batchsize = 1000;
list<forlan> allresults = new arraylist<>();
boolean hasmoreresults = true;

forlan forlan = service.query().select("min(id) id").one();
if (forlan == null) {
	return allresults;
}
long lastid = forlan.getid() - 1;

while (hasmoreresults) {
	try {
		list<forlan> batchresults = service.query().gt("id", lastid).last("limit " + batchsize).list();
		if (batchresults == null || batchresults.isempty()) {
			hasmoreresults = false;
		} else {
			allresults.addall(batchresults);
			lastid = batchresults.get(batchresults.size() - 1).getid();
		}
	} catch (exception e) {
		log.info("error querying batch: ", e);
		hasmoreresults = false;
	}
}
return allresults;

2)书签法

是目前性能最优的方案。它摒弃了offset,通过上一页最后一条记录的唯一键值来定位下一页的起始位置,但要求主键或查询条件连续

假设我们按自增id排序,上一页返回的最后一条记录id为100000。不使用offset,而是利用上一页的id进行定位

select * from products
where id > 100000
order by id asc
limit 10;

优点:查询性能恒定,不受分页深度影响,速度极快。

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持代码网。

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2026  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com