网站服务器备案查询,深圳网站制作公司方案,活动策划公司网站,推荐5家概要
分页功能是比较常见的基础功能#xff0c;虽然比较简单#xff0c;但是每次需要用到这个功能的时候还是需要现写一遍。为了实现更加宏观的业务复用#xff0c;特将本人特别喜欢的简易分页逻辑在此记述#xff0c;以备日后重用。
逻辑描述
一般的分页实现方式多是通…概要
分页功能是比较常见的基础功能虽然比较简单但是每次需要用到这个功能的时候还是需要现写一遍。为了实现更加宏观的业务复用特将本人特别喜欢的简易分页逻辑在此记述以备日后重用。
逻辑描述
一般的分页实现方式多是通过SQL语句“LIMIT”子句进行分页的如果不清楚LIMIT子句的同学还请先行了解此子句。
实际上分页功能最重要的两个参数就是pageSize每页条数和pageWant请求页码这两个参数都是int型。
pageSize自不必多说我来说说pageWant我们常用的“上一页”“下一页”、以及“跳到...页”当然对于上一页和下一页的情况页面需要维护一个全局的当前页的变量每次请求后都需要更新这个当前页的变量那么再次发起请求的时候上一页就是当前页减一下一页就是当前页加一都是通过这个参数来请求后端的。这基本解决了前端数据请求的绝大多数情况。另外前端可能需要的几个重要的数值比如总页数总条数 都可以由后台根据相关参数计算生成。
首先我们可以先定义一个返回值的封装类这个类中包含了页面分页请求后需要得到的全部数据。
然后在controller接口的参数列表中设置int pageSize 和 int pageWant这里注意pageSize如果页面可选则传入如果就固定条数甚至可以在后台直接写死即可。
紧接着将两个分页参数和其他筛选条件一同传入DAO层由SQL语句直接进行操作和计算。
最后将返回值封装为我们一开始定义的封装类中直接返回到页面即可。
功能实现
定义返回Wrapper类型
我们的返回值类型中包含页面所需的全部信息包括基本的总页数总条数请求的页码每页条数以及最重要的经过筛选条件过滤之后的单页记录列表。如下所示
import java.util.List;public class PageForDataListT {/** 每页条数*/private int pageSize;/** 数据总条数*/private int dataAmount;/** 总页数*/private int pageAmount;/** 请求页数*/private int wantPage;/** 单页记录列表list*/private ListT dataList;public PageForDataList() {}public PageForDataList(int pageSize, int dataAmount, int pageAmount, int wantPage, ListT 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 ListT getDataList() {return dataList;}public void setDataList(ListT dataList) {this.dataList dataList;}
}
设置接口参数
根据页面中的筛选条件的不同参数有多有少有不同的情况但是基本都是如下这样的结构 ApiOperation(获取特价推荐门票列表返回值为json结构体)GetMapping(value /special_price/ticket/list)public PageForDataListSpecialTicketPrice 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 PageForDataListSpecialTicketPrice ticketList(int pageSize, int wantPage, String scenicName,String scenicLocation, Integer rmdStatus, Double rateStart, Double rateEnd) {// 直接调用DAO中的查询SQLPageForDataListSpecialTicketPrice pdl mngDao.findSpecialTicketList(pageSize, wantPage, scenicName,scenicLocation, rmdStatus, rateStart, rateEnd);return pdl;}
紧接着DAO层的关键实现代码如下 /*** 分页查询特价推荐门票列表* br作者 mhtbr * 时间2018年5月7日-上午11:07:51br* return*/public PageForDataListSpecialTicketPrice 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);// 查询列表ListSpecialTicketPrice list jdbc.query(sqlBuilder.toString(),new BeanPropertyRowMapper(SpecialTicketPrice.class));// 查询dataAmount数据总条数int dataAmount jdbc.queryForObject(countSql, int.class);return new PageForDataListSpecialTicketPrice(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 23....这样的正整数。
然后我们通过jdbcTemplate来对列表查询和COUNT查询的两条SQL语句进行分别查询并赋值给list和dataAmount从而得到单页的数据列表和符合条件的总条数。
最后return的时候我直接通过最开始定义的返回值封装类的构造器将我们得到的数据进行封装返回到controller层。
其中需要通过数学函数 Math.ceil() 求得的总页数是这样的
(int) Math.ceil(1.0 * dataAmount / pageSize)
这句话的意思是用总条数dataAmount除以每页条数pageSize然后由于除不尽的原因我们需要事先将dataAmount变换成浮点型数据然后这样我们就可以得到一个double类型的数据再通过 Math.ceil() 函数将浮点型数向上取整再强转int得到结果。比如dataAmount 19pageSize 10那么如上表达式的结果应该是2也就是总共两页。
最后我们拿到了这样一个封装好的数据传给controller再通过return返回给页面即可。
最终效果展示 由于是测试数据因此数据并不多我们可以通过执行的SQL在数据库中做同样的查询看一下结果 综上就是对分页功能的简单实现。
如有疑问欢迎文末留言。