Backend Development 19 min read

Integrating PageHelper with SpringBoot and MyBatis for Efficient Pagination

This article provides a comprehensive guide on integrating PageHelper with SpringBoot and MyBatis, covering development preparation, dependency configuration, basic and advanced usage patterns, code examples, and detailed explanations of pagination mechanisms, including PageParam design and MyBatis interceptor internals.

Top Architect
Top Architect
Top Architect
Integrating PageHelper with SpringBoot and MyBatis for Efficient Pagination

1. Development Preparation

1.1 Development Tools

IntelliJ IDEA 2020.2.3

1.2 Development Environment

Red Hat Open JDK 8u256

Apache Maven 3.6.3

1.3 Development Dependencies

SpringBoot

MyBatis

PageHelper

<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> 1234 <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.1.3</version> </dependency> 12345 <dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper-spring-boot-starter</artifactId> <version>1.3.0</version> </dependency> 12345

2. Technical Documentation

2.1 SpringBoot Resources

Official site: https://spring.io/projects/spring-boot

Chinese community: https://springboot.io/

2.2 MyBatis Resources

Official site: https://mybatis.org/mybatis-3/zh/index.html

2.3 PageHelper Repository

GitHub: https://github.com/pagehelper/Mybatis-PageHelper

3. Application Explanation

3.1 Basic Usage

In real projects, PageHelper can be used with just PageInfo and PageHelper classes to achieve pagination. The most common usage is:

public PageInfo
page(RequestParamDto param) {
    PageHelper.startPage(param.getPageNum(), param.getPageSize());
    List
list = mapper.selectManySelective(param);
    PageInfo
pageInfo = (PageInfo
)list;
    return pageInfo;
}
123456

The rule is to call PageHelper.startPage(pageNum, pageSize) before the query and avoid executing other SQL in between.

3.2 Advanced Usage

A more concise form uses the default method doSelectPageInfo :

public PageInfo
page(RequestParamDto param) {
    return PageHelper.startPage(param.getPageNum(), param.getPageSize())
        .doSelectPageInfo(() -> list(param));
}

public List
list(RequestParamDto param) {
    return mapper.selectManySelective(param);
}
1234567

FAQ

1. Why declare a list method again?

Separating pagination and collection queries allows the same service to provide both paginated and non‑paginated results, improving decoupling and reusability.

2. What is doSelectPageInfo ?

It is a default method of the Page object returned by PageHelper.startPage() . It accepts a lambda that performs the query, then automatically converts the result to PageInfo without extra conversion code.

3. Why does this code look longer?

When a separate list method already exists, the lambda version keeps the pagination logic isolated, which can be clearer in complex business scenarios.

3.3 Base Service Interface

public interface BaseService
{
    default PageInfo
page(PageParam
param) {
        return PageHelper.startPage(param).doSelectPageInfo(() -> list(param.getParam()));
    }
    List
list(Param param);
}
1234567891011121314151617181920212223242526272829

The interface uses a default method (Java 8 feature) to provide a generic pagination implementation, while concrete services only need to implement the list method.

3.4 PageParam Class

public class PageParam
implements IPage {
    private Integer pageNum = 1;
    private Integer pageSize = 20;
    private String orderBy;
    private T param;
    public PageParam
setOrderBy(String orderBy) {
        this.orderBy = orderBy;
        return this;
    }
}
12345678910111213141516171819202122232425

PageParam separates pagination parameters from business parameters and implements IPage to avoid reflection‑based extraction.

3.5 Service and Controller Implementation

public interface TemplateService extends BaseService
{}
12345678
@Service
@RequiredArgsConstructor
public class TemplateServiceImpl implements TemplateService {
    private final TemplateMapper mapper;
    @Override
    public List
list(TemplateReqDto param) {
        return mapper.selectManySelective(param);
    }
}
12345678910111213141516171819202122
@RestController
@RequiredArgsConstructor
public class TemplateController {
    private final TemplateService service;
    @PostMapping(path = "page")
    public PageInfo
page(@RequestBody PageParam
pageParam) {
        return service.page(pageParam);
    }
    @PostMapping(path = "list")
    public List
list(@RequestBody Param listParam) {
        return service.list(listParam);
    }
}
1234567891011121314151617181920212223242526272829

4. Source Code Analysis

4.1 PageHelper.startPage

public static
Page
startPage(Object params) {
    Page
page = PageObjectUtil.getPageFromObject(params, true);
    Page
oldPage = getLocalPage();
    if (oldPage != null && oldPage.isOrderByOnly()) {
        page.setOrderBy(oldPage.getOrderBy());
    }
    setLocalPage(page);
    return page;
}
123456789

The method creates a Page object from the supplied parameters, merges any previous orderBy only pagination, and stores the page in a thread‑local variable.

4.2 PageObjectUtil.getPageFromObject

public static
Page
getPageFromObject(Object params, boolean required) {
    if (params == null) {
        throw new PageException("Unable to obtain pagination 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) {
                page.setOrderBy(pageParams.getOrderBy());
            } else {
                page = new Page();
                page.setOrderBy(pageParams.getOrderBy());
                page.setOrderByOnly(true);
            }
        }
        return page;
    } else {
        // omitted: reflection‑based extraction of pageNum, pageSize, orderBy
    }
}
1234567891011121314151617181920212223

If the object implements IPage , the method directly reads pagination fields; otherwise it falls back to reflection, which is more costly.

4.3 PageMethod (thread‑local handling)

public abstract class PageMethod {
    protected static final ThreadLocal
LOCAL_PAGE = new ThreadLocal<>();
    protected static void setLocalPage(Page page) { LOCAL_PAGE.set(page); }
    public static
Page
getLocalPage() { return (Page) LOCAL_PAGE.get(); }
    // other utility methods omitted
}
1234567891011121314151617

4.4 doSelectPageInfo

public
PageInfo
doSelectPageInfo(ISelect select) {
    select.doSelect();
    return this.toPageInfo();
}
1234

The lambda passed to doSelectPageInfo triggers the MyBatis interceptor, which rewrites the SQL to include pagination and a count query, then returns a populated PageInfo object.

4.5 PageInterceptor

@Intercepts({
    @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),
    @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class})
})
public class PageInterceptor implements Interceptor {
    private volatile Dialect dialect;
    private String countSuffix = "_COUNT";
    private Cache
msCountMap = null;
    private String default_dialect_class = "com.github.pagehelper.PageHelper";
    // intercept method implementation omitted for brevity
}
1234567891011121314151617181920212223

The interceptor rewrites the original query, adds a count query, and applies the appropriate pagination syntax for the underlying database.

5. Summary

PageHelper is a lightweight, widely‑used pagination plugin for MyBatis. By understanding its core classes ( PageHelper , Page , IPage ) and the interceptor mechanism, developers can efficiently implement pagination, avoid reflection overhead, and extend the framework for custom needs.

For further exploration, consider diving into the source of the MyBatis interceptor or contributing enhancements to the open‑source project.

backendjavaMyBatispaginationSpringBootPageHelper
Top Architect
Written by

Top Architect

Top Architect focuses on sharing practical architecture knowledge, covering enterprise, system, website, large‑scale distributed, and high‑availability architectures, plus architecture adjustments using internet technologies. We welcome idea‑driven, sharing‑oriented architects to exchange and learn together.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.