在某些场景,PageHelper无法获取正确的总数(total)。
PageHelper一般使用时使用的是 PageHelper.startPage(pageNum, pageSize)。其工作原理是拦截此方法后第一个查询,对其进行分页,并自动解析sql ,拼接出一个查询数量的sql并执行,最后将两个查询(一个分页一个总数)的结果封装为一个 Page
若因为某些原因,此Page对象丢失,则对应的total信息也就丢失了。此时,再使用 pageInfo = new PageInfo(alarmStrategies); 生成对应的 PageInfo 时,只能从 list 中获取所谓的total,即list的size。因此某些比较复杂的业务场景total的数值始终是不对的。
在业务场景很简单的情况下,也许可能出现数据经过查询后,从 Mapper 一路路过了 Service 直接给到了 Controller 的情况。此时,PageHelper可以正常工作,并返回正确的结果。
但,需要此场景的人反思:
另外,所谓的业务场景很简单其实是伪命题,按道理来说,绝大部分场景来说,都应该踩坑才是对的:
上述分析并不是否认DTO的合理性,若因种种原因,项目开发时约定,数据模型设计从简、或使用DTO等方式是无可厚非的。只要不是业务较复杂、数据较多变的项目里,应为没有提前约定,且研发人员普遍因为种种原因(对没错,基本就是因为懒)导致后端模型一路扔出后端即可。
目前常见(传抄)的处理方式是sql执行前开启分页,sql执行后直接包装页面,同时强调后面两句话之间不要插入其他业务
参考 §3.2 ,这其实并没有解决问题,依然存在模型转换时丢失的情况(某些场景下,因为接口的原因这个方式其实是行不通的,原因已经说过两次了)。此方法可以解决total不准确的问题,其实是通过强行将业务按在所谓的简单业务场景
同时反过来思考,这只是一个辅助我们分页用的小工具,为什么还要影响到我们怎么写业务?理想的辅助框架是:我们怎么写业务随便,符合某些接口上的约定即可,然后简单的进行一下框架的配置——它生效了!
另一种处理是手动补充对总数的查询,并最后将总数塞入PageInfo中。
这是一种虽然看着和写着都比较恶心(因为还得自己补一个取total的语句,还得自己调),但本质上远好于刚刚那个方法的处理方式,因为在本质上——不影响写业务!
但,依然存在问题:
至少在mapper层,正确的total信息是会随着实际的 Page
一般,分页用于前端列表或对应功能的开放接口,通常带有其他查询参数,还经常遇到卡时间段的情况,当然分页的两个参数(页号、页容量)也是大宝天天见的状态。
因此,可以提供一种分页条件查询基类,里面是时间段字段、分页字段、操作人字段等,那么再把 total 作为一个字段扔进去也是勉强可以的。
同时,对于每一个复杂查询,提供对应的分页条件查询子类,当然,继承基类,这种类统一命名为QueryCondition。其数据由前端组织发送,SpringMvc(或其他框架)统一接收,广大研发同学不需要特别在意。
PS: QueryCondition 本身就是建议在项目除推广的约定的一部分,对于后端研发,约定优于编码
同时,此类型的参数最终是会直接传递给mapper层的(因为带着查询参数),因此只需要把total塞到这个对象中就可以把总数信息通知调用方。当然,有严谨的同学会说controller接收到的参数,和mapper入参的参数也不一定是同一个。可以写一个转换工具,在两方之间进行同步参数值,但最终尽量将总数塞回去,以方便统一处理,详见后文。
只有标红的才是日常研发需要做的,其他步骤属于项目的基本配置
GeneralPageCondition 和上文说的 QueryCondition是一回事
PS: 这一步虽然是日常开发要做的,但其实意义已经不大了。因为PageHelper的限制,最后封装PageInfo已经属于脱裤放屁的操作。
对于一个分页,最关键的数据列表和总数都已经拿到了,是否封装为PageInfo,其实没有必要,而强行封为PageInfo,反而影响接口的自由定义不利于项目或团队的自定义规范或约定或统一报文