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.
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;
}
123456The 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);
}
1234567FAQ
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);
}
1234567891011121314151617181920212223242526272829The 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;
}
}
12345678910111213141516171819202122232425PageParam 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);
}
}
12345678910111213141516171819202122232425262728294. 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;
}
123456789The 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
}
}
1234567891011121314151617181920212223If 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
}
12345678910111213141516174.4 doSelectPageInfo
public
PageInfo
doSelectPageInfo(ISelect select) {
select.doSelect();
return this.toPageInfo();
}
1234The 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
}
1234567891011121314151617181920212223The 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.
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.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.