JSP数据库查询分页怎么做,分页查询有哪些方法?
- 物理机
- 2026-08-08
- 5
JSP分页查询的核心逻辑是先算总数再切数据段,用LIMIT(MySQL)或ROWNUM(Oracle)控制每页返回的记录范围,配合当前页码动态拼接SQL,就能在绝大多数业务系统中跑通稳定高效的分页方案。
JSP分页查询到底在解决什么问题
做Java Web开发的同行应该都有体会,JSP页面本身不负责数据处理,真正干活的往往是后端的Servlet和DAO层,分页查询的场景在实际项目中太常见了,商品列表、订单记录、用户管理、日志审计,只要是数据量超过几百条的列表页面,几乎都得做分页。
不上分页的后果很明显:数据一次性全查出来,页面渲染卡顿,浏览器内存吃紧,用户体验极差,更麻烦的是,数据库侧也会因为一次查询返回大量无用数据而增加I/O压力,行业共识认为,分页查询是Web开发中性价比最高的性能优化手段之一,代码改动量不大,但效果立竿见影。
分页查询的两种主流实现思路
物理分页:真正只查当前页的数据
物理分页的核心逻辑很简单:SQL语句本身就带上了分页条件,数据库只返回当前页需要的那几条记录,比如MySQL用LIMIT关键字,Oracle用ROWNUM或者FETCH FIRST子句,SQL Server用OFFSET...FETCH。
这种方式的优点非常突出:内存占用小,查询速度快,数据量再大也不会把内存撑爆,缺点是SQL语句需要根据不同的数据库方言做调整,代码可移植性稍微差一些。
逻辑分页:先全查出来再在内存里截取
逻辑分页就是把所有满足条件的数据一次性查出来,然后在Java代码里通过List.subList()方法截取当前页需要的数据,这种方式实现起来确实简单,但性能隐患很大——数据量一旦上来,内存开销会非常恐怖。
说实话,我见过不少刚入行的开发者在初期图省事用逻辑分页,结果数据量到几万条的时候页面直接卡死。逻辑分页只适合数据量极小(比如几百条以内)的后台配置类页面,业务主流程的分页查询还是老老实实走物理分页吧。
手写JDBC分页查询的完整思路
业内专家指出,理解手写分页的过程比直接套用框架更重要,因为框架的底层逻辑也是这个套路,下面以MySQL为例,拆解一下完整的实现步骤。
第一步:明确分页参数
分页需要两个核心参数:currentPage(当前页码)和pageSize(每页条数),这两个参数通常由前端页面传递过来,后端接收后做合法性校验。
int currentPage = Integer.parseInt(request.getParameter("currentPage")); int pageSize = Integer.parseInt(request.getParameter("pageSize")); // 防御性处理 if (currentPage < 1) currentPage = 1; if (pageSize < 1) pageSize = 10;
第二步:计算起始位置
MySQL的LIMIT语法是LIMIT offset, count,其中offset表示跳过多少条记录,计算公式是:
offset = (currentPage 1) pageSize
比如第1页每页10条,offset就是0;第2页的offset就是10;第3页是20,这个公式是分页查询的基石,几乎所有数据库的分页逻辑都遵循这个计算规则。
第三步:查询总记录数
分页必须知道总共有多少条数据,才能计算总页数,这里需要单独执行一个COUNT()查询:
String countSql = "SELECT COUNT() FROM user WHERE status = 1"; PreparedStatement ps = conn.prepareStatement(countSql); ResultSet rs = ps.executeQuery(); int totalCount = 0; if (rs.next()) { totalCount = rs.getInt(1); } int totalPages = (totalCount + pageSize 1) / pageSize;
这里的totalPages计算用的是向上取整的数学技巧,避免最后一页数据不足一页时出现页数少算的情况。
第四步:执行分页数据查询
String dataSql = "SELECT FROM user WHERE status = 1 ORDER BY create_time DESC LIMIT ?, ?"; PreparedStatement ps = conn.prepareStatement(dataSql); ps.setInt(1, offset); ps.setInt(2, pageSize); ResultSet rs = ps.executeQuery();
注意ORDER BY子句在分页查询中非常重要。没有排序的分页查询结果是不确定的,可能导致用户翻页时看到重复数据或者遗漏数据,建议总是给分页查询加上明确的主键或时间字段排序。
第五步:封装分页结果对象
实际开发中通常会把分页相关的数据封装成一个PageBean对象,包含当前页码、每页条数、总记录数、总页数、数据列表这几个字段,这样前端拿到这个对象后,既能渲染列表数据,也能绘制分页导航条。
JSP分页和Servlet分页的区别是什么
很多初学者对“JSP分页”和“Servlet分页”这两个概念有些混淆,其实严格来说,JSP本身不擅长做分页逻辑处理,它更适合做页面展示,分页的数据获取和计算逻辑应该在Servlet或Service层完成,JSP只负责把结果渲染出来。
一个负责任的分层做法是:
- Servlet接收请求,解析分页参数
- Service层调用DAO执行总记录数查询和数据查询
- 分页结果封装成JavaBean存入request作用域
- JSP通过${pageBean.dataList}这样的EL表达式遍历输出数据
- JSP底部的分页导航条通过${pageBean.totalPages}等属性动态生成页码链接
- 小型管理后台(数据量万级以内):手写JDBC分页或者MyBatis分页插件都行,简单直接
- 中型业务系统(数据量十万到百万级):使用PageHelper等成熟分页插件,配合合理的索引设计
- 大型互联网应用(数据量千万级以上):考虑基于游标的分页方案,或者引入搜索引擎(如Elasticsearch)来分担查询压力


很多老项目直接在JSP页面里写Java脚本片段(<% %>)来拼接SQL和查询数据库,这种做法虽然能跑,但维护性极差,现在的主流做法是彻底抛弃Scriptlet,用JSTL标签和EL表达式来渲染页面。
分页查询性能慢怎么排查和优化
分页查询在数据量大的时候会遇到明显的性能瓶颈,最典型的问题是“深分页”场景,所谓深分页,就是用户翻到第100页、第1000页的时候,offset值变得很大,MySQL需要扫描并丢弃前面的大量行才能返回结果。
深分页的优化方案
延迟关联
先只查询主键ID,再用主键ID关联查询其他字段:
SELECT FROM user INNER JOIN (SELECT id FROM user ORDER BY create_time DESC LIMIT 10000, 10) AS tmp ON user.id = tmp.id
这种方式让数据库只扫描主键索引而不是全表扫描,性能提升明显。
基于游标的分页
记住上一页最后一条记录的排序字段值,下一页直接用WHERE create_time < 上一页最后一条的时间来查询,这种方式不依赖offset,数据量再大性能也不会下降,适合无限滚动加载的场景。
索引优化
分页查询的ORDER BY字段和WHERE条件字段都应该建索引,比如上面例子中的status和create_time,建立一个联合索引(status, create_time)效果会更好,索引是分页查询性能的基石,没有索引配合的分页查询在数据量上来后基本都会出问题。

JSP分页查询适用于什么规模的业务场景
实际项目选型时,需要根据业务场景来决定用哪种分页方案:
据行业统计,相当一部分Java Web项目的数据量都处于中小规模,手写分页配合插件完全够用,不需要过度设计。
分页查询的常见坑和注意事项
页码越界处理
用户手动修改URL参数,把page改成10000,但总页数只有50页,后端必须做越界校验,超过总页数时默认返回最后一页的数据,否则返回空列表会让前端报错。
数据重复问题
分页查询过程中如果底层数据发生了插入或删除操作,会导致用户翻页时出现数据重复或缺失,这个问题的根源在于分页查询不是事务性的,解决思路是尽量减少排序字段的变更,或者接受这种轻微的数据不一致(大多数业务场景可以接受)。
SQL载入风险
分页参数是通过前端传过来的,如果不做参数化查询,恶意用户可能在pageSize字段拼接SQL载入语句。必须使用PreparedStatement的参数占位符来拼接分页参数,不能直接字符串拼接SQL。
分页查询的代码封装建议
不要在每一个DAO方法里都写一遍分页逻辑,建议抽一个通用的分页工具类,或者直接用PageHelper这种开源插件。PageHelper的用法非常简单,在查询前调用:
PageHelper.startPage(currentPage, pageSize); List<User> list = userMapper.selectAll(); PageInfo<User> pageInfo = new PageInfo<>(list);
一行代码就完成了分页拦截,PageInfo对象里已经包含了总记录数、总页数、当前页码等所有分页信息,对于使用MyBatis的项目来说,这是目前最主流的分页方案。
分页查询常见问题解答
JSP分页查询怎么实现总页数显示
先执行SELECT COUNT()获取总记录数,再用(totalCount + pageSize 1) / pageSize向上取整得到总页数,前端分页条根据总页数动态生成页码链接,当前页高亮显示。
JSP分页和 Ajax 分页有什么不同
JSP分页是传统同步请求,每次翻页都会刷新整个页面,Ajax分页是在JSP基础上用异步请求只刷新列表区域,体验更流畅,但后端分页逻辑是完全一样的,两者的核心区别在于前端交互方式,后端分页SQL没有任何变化。
分页查询时数据量太大页面加载很慢怎么办
优先检查WHERE条件和ORDER BY字段是否走了索引,其次考虑深分页优化方案(延迟关联或游标分页),如果数据量超过百万级,建议引入缓存或者搜索引擎,不要把所有压力都放在数据库上,说到底,分页查询的优化核心就是减少数据库扫描的数据量,所有方案都是围绕这个原则展开的。