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

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

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<ResponseEntityDto> page(RequestParamDto param) {
    PageHelper.startPage(param.getPageNum(), param.getPageSize());
    List<ResoinseEntityDto> list = mapper.selectManySelective(param);
    PageInfo<ResponseEntityDto> pageInfo = (PageInfo<ResponseEntityDto>)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<ResponseEntityDto> page(RequestParamDto param) {
    return PageHelper.startPage(param.getPageNum(), param.getPageSize())
        .doSelectPageInfo(() -> list(param));
}

public List<ResponseEntityDto> 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<Param, Result> {
    default PageInfo<Result> page(PageParam<Param> param) {
        return PageHelper.startPage(param).doSelectPageInfo(() -> list(param.getParam()));
    }
    List<Result> 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<T> implements IPage {
    private Integer pageNum = 1;
    private Integer pageSize = 20;
    private String orderBy;
    private T param;
    public PageParam<T> 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<TemplateReqDto, TemplateRespDto> {}
12345678
@Service
@RequiredArgsConstructor
public class TemplateServiceImpl implements TemplateService {
    private final TemplateMapper mapper;
    @Override
    public List<TemplateRespDto> list(TemplateReqDto param) {
        return mapper.selectManySelective(param);
    }
}
12345678910111213141516171819202122
@RestController
@RequiredArgsConstructor
public class TemplateController {
    private final TemplateService service;
    @PostMapping(path = "page")
    public PageInfo<Result> page(@RequestBody PageParam<Param> pageParam) {
        return service.page(pageParam);
    }
    @PostMapping(path = "list")
    public List<Result> list(@RequestBody Param listParam) {
        return service.list(listParam);
    }
}
1234567891011121314151617181920212223242526272829

4. Source Code Analysis

4.1 PageHelper.startPage

public static <E> Page<E> startPage(Object params) {
    Page<E> page = PageObjectUtil.getPageFromObject(params, true);
    Page<E> 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 <T> Page<T> 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<Page> LOCAL_PAGE = new ThreadLocal<>();
    protected static void setLocalPage(Page page) { LOCAL_PAGE.set(page); }
    public static <T> Page<T> getLocalPage() { return (Page) LOCAL_PAGE.get(); }
    // other utility methods omitted
}
1234567891011121314151617

4.4 doSelectPageInfo

public <E> PageInfo<E> 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<String, MappedStatement> 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.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

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

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.