Spring boot + PageHelper to achieve paging, a very comprehensive summary!

Osu ab41ho2s 2020-11-10 13:38:54
spring boot pagehelper achieve paging


Simple books :https://www.jianshu.com/p/a24a9ff323c9

One . The development of preparation

1. development tool

2. development environment

3. Development depends on

  • PageHelper


Two . Technical documentation

1. be based on SpringBoot

2. be based on MyBatis

3. Integrate PageHelper

  • PageHelper Open source warehouse :https://github.com/pagehelper/Mybatis-PageHelper

Spring Boot Learning theory and practical recommendation :


3、 ... and . Application explanation

1. Basic use

In the actual project application ,PageHelper It's very convenient to use , Only through PageInfo + PageHelper Two classes , That's enough for paging , However, this is often the simplest way to use integration , But in many practical application scenarios , It's not fully exploited .

Here's how we use it most often :

public PageInfo<ResponseEntityDto> page(RequestParamDto param) {
 PageHelper.startPage(param.getPageNum(), param.getPageSize());
 List<ResoinseEntityDto> list = mapper.selectManySelective(param);
 PageInfo<ResponseEntityDto> pageInfo = (PageInfo<ResponseEntityDto>)list;
 return pageInfo;

In a way , The above writing is indeed in line with PageHelper The standard of use :

  Use... Before a collection query `PageHelper.startPage(pageNum,pageSize)`, also * You can't do anything else in between SQL*

But as a Developer In the us , Only in the pursuit of perfection and perfection can we find breakthroughs and opportunities ; The following are reasonable and standard basic uses :

public PageInfo<ResponseEntityDto> page(RequestParamDto param) {
 return PageHelper.startPage(param.getPageNum(), param.getPageSize())
     .doSelectPageInfo(() -> list(param))
public List<ResponseEntityDto> list(RequestParamDto param) {
 return mapper.selectManySelective(param);


1. Why declare one again list function ?

answer : In many practical business application scenarios , Paging query It is based on the large amount of data table display needs . But a lot of times , for example : Internal service calls to each other ,OpenAPI The supply of .

Even in some business scenarios where the front end and the back end are separated from each other , It also needs a non paged collection query interface to provide services . in addition , For the time being, the above factors will not be discussed , We can define and standardize something according to the above writing method

for example : Paging and collection queries Separation and decoupling ( For details of decoupling, see advanced use ), Paging request request and response with the actual business Separation of parameters ( For details, please see advanced use ) wait ...

2. doSelectPageInfo What is it? ?

answer : doSelectPageInfo yes PageHelper.startPage() The default returned by the function Page Instance built-in function , This function can be used to Lambda In the form of extra Function To make queries without having to do extra PageInfo And List transformation , and doSelectPageInfo The parameter of is PageHelper Built in Function(ISelect) Interface is used to achieve conversion PageInfo Purpose

3. The amount of code written in this way seems to be a lot more ?

answer : Just as a ① Described in , In terms of the amount of code , There's really no further simplification , But in some business scenarios , In the presence of list Function interface , It's a more intuitive optimization ( See advanced usage for details )

2. Advanced use

Look at the code first , Let's talk about analysis :

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import java.util.List;
 * @param <Param>  Generic request
 * @param <Result>  Generic response
public interface BaseService<ParamResult{
     *  Paging query
     * @param param  Request parameters DTO
     * @return  Pagination collection
    default PageInfo<Result> page(PageParam<Param> param) {
        return PageHelper.startPage(param).doSelectPageInfo(() -> list(param.getParam()));
     *  Set query
     * @param param  Query parameters
     * @return  Query response
    List<Result> list(Param param);

You can see BaseService As a whole Service Encapsulation and declaration of general interface , As a general paging interface page The function here uses interface Unique keywords default It's a direct statement page Method body of function body

import com.github.pagehelper.IPage;
import lombok.Data;
import lombok.experimental.Accessors;
@Data //  Use to omit redundant code lombok  There should be routine Getter/Setter Construction toString etc. 
@Accessors(chain = true//  this lombok Annotation is to implement  Entity false Build  for example : entity.setX(x).setY(y)
public class PageParam<T>  implements IPage {
    //  description = " Page number ", defaultValue =  1
    private Integer pageNum = 1;
    // description = " the number of pages ", defaultValue = 20
    private Integer pageSize = 20;
    // description = " Sort ", example = "id desc"
    private String orderBy;
    //  description = " Parameters "
    private T param;
    public PageParam<T> setOrderBy(String orderBy) {
        this.orderBy = orderBy; //  You can optimize   Let's take a look at the details of the optimization 
        return this;

stay BaseService We see a new PageParam, Refer to the PageInfo For packing / Statement / Separate paging parameters from business parameters , And the parameter type is generic , Business parameters that support any data type At the same time, you can see PageParam Realized IPage Interface , And one more orderBy attribute field

import common.base.BaseService;
import dto.req.TemplateReqDto;
import dto.resp.TemplateRespDto;
public interface TemplateService extends BaseService<TemplateReqDtoTemplateeRespDto{
    //  A fellow interface Interface ,  Business Service Just inherit BaseService
    //  And according to the actual use scenario, declare the request parameters and response results of Entity The entity can be 

in application , We just need to declare our general business query request parameters and response results

import dto.req.TemplateReqDto;
import dto.resp.TemplateRespDto;
import service.TemplateService;
import persistence.mapper.TemplateMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.List;
@Slf4j //  be based on lombok Automatic generation logger Logging instance 
@Service // SpringBoot Register in Service Bean Annotations 
@RequiredArgsConstructor //  be based on lombok According to the class final Property generation constructors   Can finish Spring Tectonic injection 
public class TemplateServiceImpl implements TemplateService {
    private final TemplateMapper mapper;
    public List<TemplateRespDto> list(TemplateReqDto param) {
        return mapper.selectManySelective(param) //  The entity can be transformed according to the actual situation 

In the implementation class, you only need to override list Method body , Write the business logic processing and query methods that need to be processed in the actual business scenario , You don't need to care about pagination

@Slf4j //  ditto 
@RestController // SpringBoot Register in Controller Bean Annotations 
@RequiredArgsConstructor //  ditto 
public class TemplateController {
    public final TemplateService service;
     *  Paging query
     * @param pageParam  Paging query parameters
     * @return  Paging query response
    @PostMapping(path = "page")
    public PageInfo<Result> page(@RequestBody PageParam<Param> pageParam) {
        return service.page(pageParam);
     *  Set query
     * @param listParam  Set query parameters
     * @return  Aggregate query response
    @PostMapping(path = "list")
    public List<Result> list(@RequestBody Param listParam) {
        return service.list(listParam);

Finally code Controller Interface , You just need to call service.page that will do , The request parameters are directly used by PageParam packing , Separate paging parameters from business parameters , In the joint debugging of front and rear interfaces , Keep this == Separation specification ==, Can greatly reduce communication and development costs .


1. BaseService As interface,page Why can a method body be declared ?

answer : Java8 One of the new features of Sinotrans is to interface Interface class added static/default Method , After the method is declared , Its subclass or implementation will have these methods by default , Can be called directly And here it is Page Method statement default Because page Functions only focus on Paging and response parameters , Out of business scenarios , Methods vary widely , So we simply define it in an abstract way , It eliminates the complex redundant process of its implementation . Official account Java The technology stack can be read Java8 Series of tutorials .

2. PageParam What's the point of this statement ? Realization IPage For what ?

answer : PageParam It's a reference PageInfo The compiled class ( Not sure. In the future PageHelper Whether it will encapsulate this kind of , Maybe I can mention Issue Up , Also involved in the development of open source frameworks ) This class is written to separate paging and business data , Let developers focus on business implementation and development , At the same time, it is also a paging query API A norm of , Both the request and the response pull out the paging related data , Use alone To achieve IPage It's because IPage As PageHelper Built in interface, Before we know more about its role , It can be used as a specification for our paging parameter declaration , and IPage Only three methods are declared in , Namely pageNum/pageSize/orderBy Of Getter Method , In addition, in the source code analysis , I'll talk about the deeper meaning of implementing this interface

3. PageParam In addition to the conventional pageNum/pageSize, Why do we need another orderBy?

answer : In a regular paging query, you only need pageNum/pageSize You can complete the purpose of pagination , But often accompanied by pagination query is also the filter sort , and orderBy Focus is based on SQL Dynamic transfer parameter sorting of

4. orderBy How to use ? Is there any problem ?

answer : orderBy and pageNum/pageSize equally , All are Pagehelper adopt MyBatis Interceptor , stay query Injected into the query , So when transmitting parameters at the front end ,orderBy Parameter should be database column desc/asc This form , For multi field sorting, you can use commas (,) Splicing , for example : columnA desc,columnB, But on the other hand, there are two problems , The first is that most database table fields are designed , Will use == Serpentine case== name , It's not in regular development == hump case== name , So there's a layer of transformation , This conversion can be assigned to the front-end parameter transfer , It can also be assigned to the back-end parameter connection . The second is to expose the sort field in the interface , Will exist order by SQL Inject The risk of , So in actual use , We need to check and check by some means orderBy Whether it is legal to pass on the reference of , For example, matching parameter values with regular expressions can only contain order by A necessary value in grammar , For example, field names ,desc or asc, Special characters are not allowed / Database keywords, etc

5. pageNum/pageSize Do you have to give a default value ?

answer : By reading PageHelper Source code , We learned that in Page Query parameters are null when , It doesn't give them default values , There is no extra processing , So that pagination fails , And give the default value , It is also to guard against all kinds of accidents that may occur in the process of debugging the interface at the front and rear end

3. Source code analysis

First let's look at PageHelper.startPage(param) What happened in the process :

public static <E> Page<E> startPage(Object params) {
 Page<E> page = PageObjectUtil.getPageFromObject(paramstrue);
 Page<E> oldPage = getLocalPage();
 if (oldPage != null && oldPage.isOrderByOnly()) {
 return page;

This is a PageHelper Inherit (extend) The abstract class of PageMethod One of the static methods in

Look at the first line of code Page<E> page = PageObjectUtil.getPageFromObject(params, true) What happened? :

public static <T> Page<T> getPageFromObject(Object params, boolean required) {
 if (params == null) {
  throw new PageException(" Unable to get paging query parameters !");
 } else if (params instanceof IPage) {
  IPage pageParams = (IPage)params;
  Page page = null;
  if (pageParams.getPageNum() != null && pageParams.getPageSize() != null) {
   page = new Page(pageParams.getPageNum(), pageParams.getPageSize());
  if (StringUtil.isNotEmpty(pageParams.getOrderBy())) {
   if (page != null) {
   } else {
    page = new Page();
  return page;
 } else {
        ... //  I've only intercepted some code fragments here ,  The above is a more important piece 

You can see in this method , Will judge first params Is it null, And then through instanceof Judge whether it is IPage A subclass or implementation class of If the above two if/else Not satisfied with , be PageHelper In the code I omit to post, I get it through a lot of reflection code pageNum/pageSize as well as orderBy. As we all know , Reflected in Java Although it is widely used , And as one of the unique features of language , Deeply loved by the majority of developers , But reflection, in a way , It's cost of performance , Even at this stage, there are many mainstream frameworks and technologies , Are trying to minimize the use of reflection , To prevent poor performance of the framework , To be eliminated from the market . So that's it , We finally explained and understood why PageParam To achieve IPage The interface , In the code here, you can get the paging parameters directly through the interface , It doesn't have to be acquired by reflection that is damaging to performance PageHelper Required parameters

Continue to look at startPage Follow up code in :

public abstract class PageMethod {
    protected static final ThreadLocal<Page> LOCAL_PAGE = new ThreadLocal();
    protected static boolean DEFAULT_COUNT = true;
    public PageMethod() {
    protected static void setLocalPage(Page page) {
    public static <T> Page<T> getLocalPage() {
        return (Page)LOCAL_PAGE.get();

You can see PageHelper Inherited abstract classes PageMethod A Page Thread local variables of , and getLocalPage() In order to get the current thread of Page And then if (oldPage != null && oldPage.isOrderByOnly()) It is to determine whether there is old paging data Here isOrderByOnly adopt getPageFromObject() Function we can know , When there is only orderBy When parameters are , That is to say true in other words , When there is old paging data and the old paging data has only sort parameters , The sorting parameters of the old paging data are included in the sorting parameters of the new paging data The new paging data is then added page Stored in local thread variables In the actual application scenario , This situation is still relatively rare , Sort only, not paginate , So in a way , We just need to understand

Let's see next doSelectPageInfo(ISelect) What happened in :

public <E> PageInfo<E> doSelectPageInfo(ISelect select) {
 return this.toPageInfo();

You can see , The implementation of this method is very simple , It's through the registration statement ISelect Interface is developed by developing a custom collection query method and executed internally , And then I went back to PageInfo Entity We mentioned that earlier ,PageHelper be based on MyBatis The interceptor achieves the purpose of pagination , So why the ISelect.doSelect() perform , I can go back PageInfo The entity ? In fact, that's what interceptors do , stay select.doSelect() Execution time , Will trigger PageHelper Self defined MyBatis Query interceptor , And through analysis SQL and SQL Parameters , According to database type , paging , for example MySQL Of limit,Oracle Of Rownum etc. , At the same time, the query defined by us SQL Before ,PageHelper It will regenerate a select count(*) Of SQL Take the lead in implementing , Has reached its definition Page The purpose of the built-in paging parameters is

    type = Executor.class,
    method "query",
    args = {MappedStatement.classObject.classRowBounds.classResultHandler.class}
), @Signature(
    type = Executor.class,
    method "query",
    args = {MappedStatement.classObject.classRowBounds.classResultHandler.classCacheKey.classBoundSql.class}
public class PageInterceptor implements Interceptor {
    private volatile Dialect dialect;
    private String countSuffix = "_COUNT";
    protected Cache<String, MappedStatement> msCountMap = null;
    private String default_dialect_class = "com.github.pagehelper.PageHelper";
    public PageInterceptor() {
    public Object intercept(Invocation invocation) throws Throwable {

The above is PageHelper Built in customization MyBatis Interceptor , Because of too much code , In order to ensure that it does not violate the principle that this blog post is irrelevant , There is no more unnecessary explanation here .


PageHelper It's not just pageNum/pageSize/orderBy These parameters , What's more pageSizeZero, reasonable Parameters are used to define more advanced pagination queries .

Four . summary

PageHelper As GitHub It's near now 10K Open source pagination framework , Perhaps the depth and breadth of the code is not as deep as the mainstream market framework and Technology , Although in the function realization and the principle , It's not very difficult to make wheels , The source code is also very clear , But to a great extent, it solves a lot of problems based on MyBatis Pagination technology problems of , Simplify and prompt the efficiency of developers , This is the direction and path that developers should aspire to and strive for in the development road .

And we as beneficiaries , It should not be just a basic use of it , Beyond development , We should also focus on the development of some frameworks , Have some understanding of the bottom layer of the framework , And expand and optimize for it

Here again put PageHelper Open source repository of !


---END--- Recommended reading 
 Code comparison tool , I'll use this 6 individual 
 Share my favorite 5 A free online  SQL Database environment , It's so convenient !
Spring Boot A combination of three moves , Hand in hand to teach you to play elegant back-end interface 
MySQL 5.7 vs 8.0, You choose the one ? Net friend : I'm going to stay where I am ~
 Last , I recommend you an interesting and interesting official account : The guy who wrote the code ,7 Old programmers teach you to write bug, reply   interview | resources Send you a complete set of Development Notes There's a surprise 
本文为[Osu ab41ho2s]所创,转载请带上原文链接,感谢

  1. 【计算机网络 12(1),尚学堂马士兵Java视频教程
  2. 【程序猿历程,史上最全的Java面试题集锦在这里
  3. 【程序猿历程(1),Javaweb视频教程百度云
  4. Notes on MySQL 45 lectures (1-7)
  5. [computer network 12 (1), Shang Xuetang Ma soldier java video tutorial
  6. The most complete collection of Java interview questions in history is here
  7. [process of program ape (1), JavaWeb video tutorial, baidu cloud
  8. Notes on MySQL 45 lectures (1-7)
  9. 精进 Spring Boot 03:Spring Boot 的配置文件和配置管理,以及用三种方式读取配置文件
  10. Refined spring boot 03: spring boot configuration files and configuration management, and reading configuration files in three ways
  11. 精进 Spring Boot 03:Spring Boot 的配置文件和配置管理,以及用三种方式读取配置文件
  12. Refined spring boot 03: spring boot configuration files and configuration management, and reading configuration files in three ways
  13. 【递归,Java传智播客笔记
  14. [recursion, Java intelligence podcast notes
  15. [adhere to painting for 386 days] the beginning of spring of 24 solar terms
  16. K8S系列第八篇(Service、EndPoints以及高可用kubeadm部署)
  17. K8s Series Part 8 (service, endpoints and high availability kubeadm deployment)
  18. 【重识 HTML (3),350道Java面试真题分享
  19. 【重识 HTML (2),Java并发编程必会的多线程你竟然还不会
  20. 【重识 HTML (1),二本Java小菜鸟4面字节跳动被秒成渣渣
  21. [re recognize HTML (3) and share 350 real Java interview questions
  22. [re recognize HTML (2). Multithreading is a must for Java Concurrent Programming. How dare you not
  23. [re recognize HTML (1), two Java rookies' 4-sided bytes beat and become slag in seconds
  24. 造轮子系列之RPC 1:如何从零开始开发RPC框架
  25. RPC 1: how to develop RPC framework from scratch
  26. 造轮子系列之RPC 1:如何从零开始开发RPC框架
  27. RPC 1: how to develop RPC framework from scratch
  28. 一次性捋清楚吧,对乱糟糟的,Spring事务扩展机制
  29. 一文彻底弄懂如何选择抽象类还是接口,连续四年百度Java岗必问面试题
  30. Redis常用命令
  31. 一双拖鞋引发的血案,狂神说Java系列笔记
  32. 一、mysql基础安装
  33. 一位程序员的独白:尽管我一生坎坷,Java框架面试基础
  34. Clear it all at once. For the messy, spring transaction extension mechanism
  35. A thorough understanding of how to choose abstract classes or interfaces, baidu Java post must ask interview questions for four consecutive years
  36. Redis common commands
  37. A pair of slippers triggered the murder, crazy God said java series notes
  38. 1、 MySQL basic installation
  39. Monologue of a programmer: despite my ups and downs in my life, Java framework is the foundation of interview
  40. 【大厂面试】三面三问Spring循环依赖,请一定要把这篇看完(建议收藏)
  41. 一线互联网企业中,springboot入门项目
  42. 一篇文带你入门SSM框架Spring开发,帮你快速拿Offer
  43. 【面试资料】Java全集、微服务、大数据、数据结构与算法、机器学习知识最全总结,283页pdf
  44. 【leetcode刷题】24.数组中重复的数字——Java版
  45. 【leetcode刷题】23.对称二叉树——Java版
  46. 【leetcode刷题】22.二叉树的中序遍历——Java版
  47. 【leetcode刷题】21.三数之和——Java版
  48. 【leetcode刷题】20.最长回文子串——Java版
  49. 【leetcode刷题】19.回文链表——Java版
  50. 【leetcode刷题】18.反转链表——Java版
  51. 【leetcode刷题】17.相交链表——Java&python版
  52. 【leetcode刷题】16.环形链表——Java版
  53. 【leetcode刷题】15.汉明距离——Java版
  54. 【leetcode刷题】14.找到所有数组中消失的数字——Java版
  55. 【leetcode刷题】13.比特位计数——Java版
  56. oracle控制用户权限命令
  57. 三年Java开发,继阿里,鲁班二期Java架构师
  58. Oracle必须要启动的服务
  59. 万字长文!深入剖析HashMap,Java基础笔试题大全带答案
  60. 一问Kafka就心慌?我却凭着这份,图灵学院vip课程百度云