概要

分页功能是比较常见的基础功能,虽然比较简单,但是每次需要用到这个功能的时候还是需要现写一遍。为了实现更加宏观的业务复用,特将本人特别喜欢的简易分页逻辑在此记述,以备日后重用。

逻辑描述

一般的分页实现方式多是通过SQL语句“LIMIT”子句进行分页的,如果不清楚LIMIT子句的同学,还请先行了解此子句。

实际上,分页功能最重要的两个参数就是pageSize(每页条数)和pageWant(请求页码),这两个参数都是int型。

pageSize自不必多说,我来说说pageWant,我们常用的“上一页”“下一页”、以及“跳到...页”(当然,对于上一页和下一页的情况,页面需要维护一个全局的当前页的变量,每次请求后都需要更新这个当前页的变量,那么再次发起请求的时候,上一页就是当前页减一,下一页就是当前页加一)都是通过这个参数来请求后端的。这基本解决了前端数据请求的绝大多数情况。另外前端可能需要的几个重要的数值,比如:总页数,总条数 都可以由后台根据相关参数计算生成。

首先,我们可以先定义一个返回值的封装类,这个类中包含了页面分页请求后需要得到的全部数据。

然后,在controller接口的参数列表中,设置int pageSize 和 int pageWant,这里注意,pageSize如果页面可选,则传入,如果就固定条数,甚至可以在后台直接写死即可。

紧接着,将两个分页参数和其他筛选条件一同传入DAO层,由SQL语句直接进行操作和计算。

最后将返回值封装为我们一开始定义的封装类中,直接返回到页面即可。

功能实现

定义返回Wrapper类型

我们的返回值类型中包含页面所需的全部信息,包括基本的总页数,总条数,请求的页码,每页条数,以及最重要的:(经过筛选条件过滤之后的)单页记录列表。如下所示:

import java.util.List;public class PageForDataList<T> {/** 每页条数*/private int pageSize;/** 数据总条数*/private int dataAmount;/** 总页数*/private int pageAmount;/** 请求页数*/private int wantPage;/** 单页记录列表list*/private List<T> dataList;public PageForDataList() {}public PageForDataList(int pageSize, int dataAmount, int pageAmount, int wantPage, List<T> dataList) {super();this.pageSize = pageSize;this.dataAmount = dataAmount;this.pageAmount = pageAmount;this.wantPage = wantPage;this.dataList = dataList;}public int getPageSize() {return pageSize;}public void setPageSize(int pageSize) {this.pageSize = pageSize;}public int getDataAmount() {return dataAmount;}public void setDataAmount(int dataAmount) {this.dataAmount = dataAmount;}public int getPageAmount() {return pageAmount;}public void setPageAmount(int pageAmount) {this.pageAmount = pageAmount;}public int getWantPage() {return wantPage;}public void setWantPage(int wantPage) {this.wantPage = wantPage;}public List<T> getDataList() {return dataList;}public void setDataList(List<T> dataList) {this.dataList = dataList;}
}

设置接口参数

根据页面中的筛选条件的不同,参数有多有少有不同的情况,但是基本都是如下这样的结构:

    @ApiOperation("获取特价推荐门票列表,返回值为json结构体")@GetMapping(value = "/special_price/ticket/list")public PageForDataList<SpecialTicketPrice> specialPriceList(@ApiParam(value = "每页条数") @RequestParam(defaultValue = "10", required = true) int pageSize,@ApiParam(value = "请求页数") @RequestParam(defaultValue = "1", required = true) int wantPage,@ApiParam(value = "景区名称") String scenicName, @ApiParam(value = "所在地") String scenicLocation,@ApiParam(value = "推荐状态") Integer rmdStatus, @ApiParam(value = "折扣起始") Double rateStart,@ApiParam(value = "折扣终止") Double rateEnd) {return sprSvc.ticketList(pageSize, wantPage, scenicName, scenicLocation, rmdStatus, rateStart, rateEnd);}

其中,参数列表前两项为分页请求的数据,其余全部是筛选条件。sprSvc是一个service。

DAO层的分页实现

由于我们是通过LIMIT子句来实现分页功能,因此不论如何,都是要将请求的页码传入SQL来操作的。实际上,Service层在一个简单的分页查询的功能中仅仅充当一个Controller层与DAO层数据交互的传递信息的角色。如下service仅供参考:

    @Overridepublic PageForDataList<SpecialTicketPrice> ticketList(int pageSize, int wantPage, String scenicName,String scenicLocation, Integer rmdStatus, Double rateStart, Double rateEnd) {// 直接调用DAO中的查询SQLPageForDataList<SpecialTicketPrice> pdl = mngDao.findSpecialTicketList(pageSize, wantPage, scenicName,scenicLocation, rmdStatus, rateStart, rateEnd);return pdl;}

紧接着DAO层的关键实现代码如下:

    /*** 分页查询特价推荐门票列表* <br>作者: mht<br> * 时间:2018年5月7日-上午11:07:51<br>* @return*/public PageForDataList<SpecialTicketPrice> findSpecialTicketList(int pageSize, int wantPage, String scenicName, String scenicLocation, Integer rmdStatus, Double rateStart,Double rateEnd) {StringBuilder sqlBuilder = new StringBuilder("SELECT a.* FROM special_ticket_price a, scenic_sequence b WHERE a.seco_scenic_id = b.seco_scenic_id ");if (scenicName != null && !scenicName.equals("")) {sqlBuilder.append("AND b.scenic_name LIKE '%" + scenicName + "%' ");}if (scenicLocation != null) {sqlBuilder.append("AND a.location = '" + scenicLocation + "' ");}if (rmdStatus != null) {sqlBuilder.append("AND a.rmd_status = " + rmdStatus + " ");}if (rateStart != null) {sqlBuilder.append("AND a.discount_rate >= " + rateStart + " ");}if (rateEnd != null) {sqlBuilder.append("AND a.discount_rate <= " + rateEnd + " ");}// 查询总条数sqlString countSql = sqlBuilder.toString().replaceAll("a.\\*", "COUNT(*)");sqlBuilder.append("ORDER BY a.seco_product_id LIMIT " + pageSize * (wantPage - 1) + "," + pageSize);// 查询列表List<SpecialTicketPrice> list = jdbc.query(sqlBuilder.toString(),new BeanPropertyRowMapper<>(SpecialTicketPrice.class));// 查询dataAmount,数据总条数int dataAmount = jdbc.queryForObject(countSql, int.class);return new PageForDataList<SpecialTicketPrice>(pageSize, dataAmount,(int) Math.ceil(1.0 * dataAmount / pageSize), wantPage, list);}

从如上代码中,我们看到,我建立了一个StringBuilder来处理单线程下的查询列表的SQL语句sqlBuilder,然后我利用联表查询,并将五个参数通过if条件拼接到sqlBuilder后。

这里注意,因为不论是什么系统,分页查询一定都是带着筛选条件之后的分页数据列表,这个很好理解,比如我们在某宝买衣服,我以“西服”+ "上衣"作为筛选条件,结果分页之后却出现了裤子、皮鞋、衬衫、内衣等等,这就完全不符合实际需求。换句话说,分页功能的实现一定是建立在筛选条件之下的一个功能

在代码中,查询总条数的SQL语句的位置很讲究:

// 查询总条数sql
String countSql = sqlBuilder.toString().replaceAll("a.\\*", "COUNT(*)");

可以看到,这句SQL是将SELECT子句中的“a.*”替换为了“COUNT(*)”用于查询符合条件的记录总条数。而其他条件不变。“\\”则是为了完成“*”的转义。

为什么说这句SQL的位置讲究?

因为它是在筛选条件拼接到sqlBuilder之后才进行总数SQL的变化,这恰恰说明了我刚才提到的,分页查询在筛选条件之后的思想。其次,也是非常重要的一点是:countSql的定义,一定要在LIMIT子句之前。换句话说,总数查询的SQL语句一定不能带LIMIT子句!稍微一思考就会明白,我们查询的COUNT(*)应该是符合条件的全部记录条数,也就是在上述代码偏后的位置定义的dataAmount变量,如果COUNT查询在LIMIT子句之后拼入SQL语句(也就是最终得到的是一个带着LIMIT子句的COUNT查询),那么我们查询的结果,也就是记录总条数dataAmount将始终会小于等于pageSize。不服的同学,可以亲自试一试。

( 还要为基础欠佳的同学补充一点的是,请求分页的LIMIT表达式应该符合如下公式:

LIMIT pageSize * (wantPage - 1) , pageSize

其中,pageSize是从前台传入的 1 ,2,3....这样的正整数。)

然后,我们通过jdbcTemplate来对列表查询和COUNT查询的两条SQL语句进行分别查询,并赋值给list和dataAmount,从而得到单页的数据列表和符合条件的总条数

最后,return的时候,我直接通过最开始定义的返回值封装类的构造器将我们得到的数据进行封装返回到controller层。

其中需要通过数学函数 Math.ceil() 求得的总页数是这样的:

(int) Math.ceil(1.0 * dataAmount / pageSize)

这句话的意思是,用总条数dataAmount除以每页条数pageSize,然后由于除不尽的原因,我们需要事先将dataAmount变换成浮点型数据,然后这样我们就可以得到一个double类型的数据,再通过 Math.ceil() 函数,将浮点型数向上取整,再强转int得到结果。比如,dataAmount = 19,pageSize = 10,那么如上表达式的结果应该是2,也就是总共两页。

最后,我们拿到了这样一个封装好的数据传给controller,再通过return,返回给页面即可。

最终效果展示

由于是测试数据,因此数据并不多,我们可以通过执行的SQL在数据库中做同样的查询看一下结果:

综上,就是对分页功能的简单实现。

如有疑问,欢迎文末留言。

数据列表的分页实现————分页敏捷开发相关推荐

  1. Mendix敏捷开发零基础学习《三》-高级 (数据删除保护机制、数据关联删除、Security安全、调用外部接口、调用JAVA代码)

    目录 Mendix敏捷开发零基础学习<三> 一. 数据保护机制(Prevention of Delete) 1.业务需求 2.业务分析 3.项目实现 二.Mendix权限(Security ...

  2. Mendix敏捷开发零基础学习《二》-进阶(Microflow微流、表单验证、运算符、条件判断、数据嵌套、触发器、Debug问题跟踪、版本管理)

    目录结构 Mendix敏捷开发零基础学习<二> 一.Microflow微流 1.引言 2.常见的功能 3.微流可以做那些事情? 3.1 举例1(用微流打开新增页面) 3.2 举例2(用微流 ...

  3. think php 数据分页,接口数据使用ThinkPHP5的自定义分页

    tp程序自带了分页的功能,用起来很是方便,特别是默认情况下直接连样式都写好了.非常的省心.但这只是对于数据库操作而言.如果我们想要对请求的接口数据进行分页,那么该如何使用paginate方法呢? 修改 ...

  4. SharePoint2010沙盒解决方案基础开发——关于TreeView树形控件读取列表数据(树形导航)的webpart开发及问题...

    转:http://blog.csdn.net/miragesky2049/article/details/7204882 SharePoint2010沙盒解决方案基础开发--关于TreeView树形控 ...

  5. php 动态分页,PHP动态分页函数,PHP开发分页必备啦

    PHP动态分页函数,PHP开发分页必备啦发布:mdxy-dxy 字体:[增加 减小] 类型:转载 PHP动态分页函数,PHP开发分页必备啦.其实吧,这个是必用的,不说了,PHP动态分页函数还是不错,比 ...

  6. dedecms织梦专题节点列表内容怎么实现分页

    这篇文章主要介绍dedecms织梦专题节点列表内容怎么实现分页,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!  dedecms织梦专题节点列表内容怎么实现分页?  方法:  打 ...

  7. ASP.Net分页组件1.0开发下载了...

    ASP.Net分页组件1.0开发下载了...支持皮肤和自定义样式.热乎乎的,写的不好的地方指正出来啊... 开源免费的.希望大家多多支持... ASP.Net分页组件1.0开发下载了...支持皮肤和自 ...

  8. 受困于敏捷开发的数据与架构?肿么办?

    戳蓝字"CSDN云计算"关注我们哦! 译|Lorraine Lo 文|Isaac Sacolick 来源|InfoWorld网站 如今企业强调敏捷开发不是一天两天,但在此过程中敏捷 ...

  9. 解决Mysql数据量大的时候 分页优化(使用limit)的问题

    解决Mysql数据量大的时候 分页优化(使用limit)的问题 参考文章: (1)解决Mysql数据量大的时候 分页优化(使用limit)的问题 (2)https://www.cnblogs.com/ ...

最新文章

  1. centos7安装mongodb3.4
  2. pandas dataframe 表头_python_库_pandas
  3. mp4格式解析、分割
  4. C++ 有参构造 无参构造 拷贝构造 以及参数化列表 成员对象之间的执行关系
  5. 使用dubbo后尽量不用要@Reference可能引起冲突
  6. python mysql删除数据_python-mysql删除和更新数据
  7. graphics | 基础绘图系统(九)——栅格图、点密度图、等高线(填充)图、三维图...
  8. 分析师:BTC既是通胀对冲工具 也是有指数级增长潜力的资产
  9. [Android]应用语言切换的三种方法
  10. Arduino UNO+TB6600驱动器控制步进电机正反转
  11. 剪贴板查看器clipbrd.exe
  12. mysql SELECT/UPDATE command denied to user 'root'@'localhost' for table 'XXX' 报错1142处理
  13. 光线追踪(RayTracing)算法理论与实践(三)光照
  14. ios:更改app名称
  15. VMware虚拟机安装黑群晖系统
  16. hive 元数据 解释
  17. SpringBoot通过WorkBook快速实现对Excel的导入和导出(包括数据校验)
  18. WEB服务及WSDL
  19. 汽车后市场助理—行驶证识别
  20. 股票量化分析工具QTYX使用攻略——实盘交易信号监控(更新2.5.7)

热门文章

  1. Java类class isAnnotationPresent()方法与示例
  2. 面经 | 我是如何拿到阿里offer的?附面试题+视频
  3. 十、封装python3读写ini文件类
  4. Python禁止最大化按钮和禁止拉伸窗口大小
  5. Kubernetes 1.20 报错:“open /run/flannel/subnet.env: no such file or directory“
  6. mysql导入工具 行提交_使用命令行工具mysqlimport导入数据
  7. JQuery DataTables改变行或列的背景或字体颜色
  8. 数据封装以及解封的过程
  9. java 字符串实例_Java字符串实例
  10. linux fcntl 设置阻塞,Linux fcntl函数设置阻塞与非阻塞