全栈角度看分页处理
分页是 web application 开发最常见的功能。在使用不同的框架和工具过程中,发现初始行/页的定义不同,特意整理记录。从这个技术点去看不同层的实现。以及不同语言实现的对比。
文章会从正常的web 结构分层的角度去梳理不同层的处理,分为数据库分页、服务端分页、前端分页三部分。
一、数据库分页
这里用mysql 举例整理。我们常用的数据库例如 Oracle/ SQL Server 等,对于分页语法的支持大同小异。不做具体一一举例。
先从数据库层梳理,也是从最根源去分析分页的最终目的,前端和后端的一起逻辑和适配,都是为了拼接合适的 SQL 语句。
1.MySQL LIMIT
语法:[LIMIT {[ offset ,] row_count }]
LIMIT row_count is equivalent to LIMIT 0, row_count.
The offset of the initial row is 0 (not 1)
参考:MySQL :: MySQL 5.7 Reference Manual :: 13.2.9 SELECT Statement
二、服务端/后端分页
后端分页,简单讲,就是数据库的分页。对于mysql 来讲,就是上述 offset row_count 的计算过程。
这里选用了常用的框架组件来对比各自实现的细节。
pagehelper 是Java Orm 框架mybatis 常用的开源分页插件
spring-data-jdbc 是Java 框架常用的数据层组件 1.pagehelper/** * 计算起止行号 offset * @see com.github.pagehelper.Page#calculateStartAndEndRow */ private void calculateStartAndEndRow() { // pageNum 页码,从1开始。pageNum < 1 , 忽略计算。 this.startRow = this.pageNum > 0 ? (this.pageNum - 1) * this.pageSize : 0; this.endRow = this.startRow + this.pageSize * (this.pageNum > 0 ? 1 : 0); }/** * 计算总页数 pages/ pageCount。 * 在赋值数据总条数的同时,也计算了总页数。 * 可以与 Math.ceil 实现对比看。 */ public void setTotal(long total) { if (pageSize > 0) { pages = (int) (total / pageSize + ((total % pageSize == 0) ? 0 : 1)); } else { pages = 0; } }
SQL 拼接实现:com.github.pagehelper.dialect.helper.MySqlDialect 2.spring-data-jdbc
关键类:
org.springframework.data.domain.Pageable
org.springframework.data.web.PageableDefault /** * offset 计算,不同于pagehelper, page 页码从0 开始。default is 0 * @see org.springframework.data.domain.AbstractPageRequest#getOffset */ public long getOffset() { return (long)this.page * (long)this.size; } /* * 总页数的计算使用 Math.ceil 实现。 * @see org.springframework.data.domain.Page#getTotalPages() */ @Override public int getTotalPages() { return getSize() == 0 ? 1 : (int) Math.ceil((double) total / (double) getSize()); }/** * offset 计算,不同于pagehelper, page 页码从0 开始。 * @see org.springframework.data.jdbc.core.convert.SqlGenerator#applyPagination */ private SelectBuilder.SelectOrdered applyPagination(Pageable pageable, SelectBuilder.SelectOrdered select) { // 在spring-data-relation, Limit 抽象为 SelectLimitOffset SelectBuilder.SelectLimitOffset limitable = (SelectBuilder.SelectLimitOffset) select; // To read the first 20 rows from start use limitOffset(20, 0). to read the next 20 use limitOffset(20, 20). SelectBuilder.SelectLimitOffset limitResult = limitable.limitOffset(pageable.getPageSize(), pageable.getOffset()); return (SelectBuilder.SelectOrdered) limitResult; }
spring-data-commons 提供 mvc 层的分页参数处理器 /** * Annotation to set defaults when injecting a {@link org.springframework.data.domain.Pageable} into a controller method. * * @see org.springframework.data.web.PageableDefault */ @Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.PARAMETER) public @interface PageableDefault { /** * The default-size the injected {@link org.springframework.data.domain.Pageable} should get if no corresponding * parameter defined in request (default is 10). */ int size() default 10; /** * The default-pagenumber the injected {@link org.springframework.data.domain.Pageable} should get if no corresponding * parameter defined in request (default is 0). */ int page() default 0; }
MVC 参数处理器:org.springframework.data.web.PageableHandlerMethodArgumentResolver
三、前端分页
前端展示层,分别从服务端渲染方案以及纯前端脚本方案去看分页最终的页面呈现逻辑。
这里选取的分别是Java 常用的模板引擎 thymeleaf 以及热门的前端框架 element-ui。
从用法以及组件源码角度,去理清终端处理分页的常见方式。 1.thymeleaf - 模板引擎
Thymeleaf is a modern server-side Java template engine for both web and standalone environments. 2.element-ui 前端框架// from node_moduleselement-uipackagespaginationsrcpagination.js // page-count 总页数,total 和 page-count 设置任意一个就可以达到显示页码的功能; computed: { internalPageCount() { if (typeof this.total === "number") { // 页数计算使用 Math.ceil return Math.max(1, Math.ceil(this.total / this.internalPageSize)); } else if (typeof this.pageCount === "number") { return Math.max(1, this.pageCount); } return null; } }, /** * 起始页计算。page 页码从1 开始。 */ getValidCurrentPage(value) { value = parseInt(value, 10); // 从源码的实现可以看到,一个稳定强大的开源框架,在容错、边界处理的严谨和思考。 const havePageCount = typeof this.internalPageCount === "number"; let resetValue; if (!havePageCount) { if (isNaN(value) || value < 1) resetValue = 1; } else { // 强制赋值起始值 1 if (value < 1) { resetValue = 1; } else if (value > this.internalPageCount) { // 数据越界,强制拉回到PageCount resetValue = this.internalPageCount; } } if (resetValue === undefined && isNaN(value)) { resetValue = 1; } else if (resetValue === 0) { resetValue = 1; } return resetValue === undefined ? value : resetValue; }
四、总结
技术永远是关联的,思路永远是相似的,方案永远是相通的。单独的去分析某个技术或者原理,总是有边界和困惑存在。纵向拉伸,横向对比才能对技术方案有深刻的理解。在实战应用中,能灵活自如。
分页实现的方案最终是由数据库决定的,对于众多的数据库,通过SQL 语法的规范去框定,以及我们常用的各种组件或者插件去适配。
纵向对比,我们可以看到不同技术层的职责和通用适配的实现过程,对于我们日常的业务通用开发以及不同业务的兼容有很大的借鉴意义。
横向对比,例如前端展示层的实现思路,其实差别非常大。如果使用 thymeleaf,结构简单清晰,但交互响应上都会通过服务器。如果选用element-ui,分页只依赖展示层,和服务端彻底解构。在技术选型中可以根据各自的优缺点进行适度的抉择。
作者:杨攀
来源:微信公众号:京东云开发者
出处:https://mp.weixin.qq.com/s/u3K7LjRNQOVQdfGxzjkvwQ
年卡突然取消!上海迪士尼发公告没得不限次数进场了!3月17日消息,上海迪士尼乐园通过官方小程序发布了年卡暂停办理通告,于今日上午8点开始暂停所有形式的年卡办理,但年卡续费不受影响。据悉,上海迪士尼共有无限钻石卡星光钻石卡和梦幻水晶
魔神劫火燃战九霄逍遥情缘全新资料片上线,一起迎战蚩尤吧!魔神劫火,燃战九霄!最近,魔神蚩尤得到了新的力量,觉醒了全新的形态,这也意味着将有更大的挑战等待着少侠们去面对。同时,逍遥情缘以开放一个新服务器势如破竹,并准备了大量的福利活动等待
拳皇(KingofFighters)红白机游戏拳皇(KingofFighters,缩写为KOF)是由日本SNK公司开发的一款格斗游戏系列,于1994年首次发布。该系列以固定画面的2D格斗为主要特点,包含了大量的角色和战斗场景。
逍遥情缘手游打架累了就来欢乐棋局对弈一把吧游玩逍遥情缘手游,战斗多了总会有疲倦的时候,这时候我们不妨换换口味,来一场不依靠角色和宠物实力的欢乐棋局吧。这是最近新上线的一个休闲玩法,可以跟其他玩家下棋对弈,棋局的胜负主要依赖
巴基斯坦女孩和你握手是啥意思?导游先别高兴,你可能回不去了自从我们国家开始改革开放的国策之后,国家的经济发展有了快速的发展,而经济的发展又促进了百姓生活水平的提高,现在,人们不仅生活好了,收入也更多了。(此处已添加小程序,请到今日头条客户
黑帮大佬杜月笙?在他面前,也只能称小弟上期我们讲到了杜月笙,这位上个世纪上海滩的黑道传奇,叱咤风云好不威风。但是今天要讲述的这个人,地位之显赫,连杜月笙都要尊称大哥,他就是上海滩虹口三杰之首,黄金荣,今天就让我们一起,
同比下降173月112日国内乘用车销量出炉!进入2023年,由于各种原因的影响,国内汽车产业的消费趋势并没有意料之中的增长,反而跌幅较大。自1月1日起,全国乘用车市场累计零售309。4万辆,同比去年下降19。不过,新能源汽车
杭氧股份,世界级隐形冠军!杭氧股份,我是2018年开始关注的,这公司很有意思,从2010上市以后的十年期间,股价一直处于低位震荡,没有上涨,直到2020年,股价开始进入了上升期。但事实上呢,杭氧股份的业绩一
孔明珠啦啦物语啦啦在幼儿园上学,喜欢唱歌跳舞,喜欢游泳滑雪,是个汉语和英语轮流切换的5岁小女孩。(一)OK小谷啦啦家里有一只小谷声控智能音响,摆在屋角的小茶几上,是个淡灰色圆圆的东西,爸爸经常叫
以存量换增量,苏州相城探索存量空间盘活新路径日前,在苏州市相城经开区高端智能制造产业园区内,谈浜路徐家观路泗荡泾路三条内部道路顺利完工通车,产业园内的毛细血管进一步畅通。这里曾经是散乱污整治的重点区域,如今已成为经开区三城建
WTT大满贯17日赛程!王楚钦内战林高远,女单力争包揽四强北京时间3月16日,2023年WTT大满贯赛新加坡站正赛第6个比赛日结束。该比赛日进行了男单下半区的18决赛,女单上半区的18决赛,混双决赛,以及男女双打的14决赛。自此,男女单打