Skip to content

How to Implement Pagination with MyBatis-Flex: Page Queries and Metadata

I needed to display a large dataset in pages rather than loading everything at once. With MyBatis-Flex, I found the paginate() method handles both data retrieval and count calculations in one call.

Basic Pagination

I wanted to get accounts with a specific filter, split into pages. Here’s how I did it:

AccountMapper.java
public interface AccountMapper extends BaseMapper<Account> {
// The paginate method is inherited from BaseMapper
}
PaginationExample.java
// Create a query wrapper with filter condition
QueryWrapper queryWrapper = QueryWrapper.create()
.where(ACCOUNT.AGE.ge(18));
// Call paginate: pageNumber=1, pageSize=2
Page<Account> page = accountMapper.paginate(1, 2, queryWrapper);
// Get the records for current page
List<Account> records = page.getRecords();
// Get pagination metadata
long totalRow = page.getTotalRow(); // Total matching records
long totalPage = page.getTotalPage(); // Total pages

When I ran this with 5 accounts matching the filter:

Records on page 1: 2
Total matching records: 5
Total pages: 3

The Page<T> object contains both the data and metadata, so I didn’t need to write a separate count query.

Service Layer Method

I wrapped the pagination logic in a service method:

AccountService.java
public class AccountService {
private final AccountMapper accountMapper;
public AccountService(AccountMapper accountMapper) {
this.accountMapper = accountMapper;
}
public Page<Account> findAdultAccounts(int pageNumber, int pageSize) {
QueryWrapper queryWrapper = QueryWrapper.create()
.where(ACCOUNT.AGE.ge(18));
return accountMapper.paginate(pageNumber, pageSize, queryWrapper);
}
}

This keeps the pagination logic reusable across different controllers.

Controller with Pagination Metadata

In my controller, I used the Page object to build a response with all the information the frontend needs:

AccountController.java
@GetMapping("/accounts")
public Map<String, Object> getAccounts(
@RequestParam(defaultValue = "1") int page,
@RequestParam(defaultValue = "10") int size) {
Page<Account> result = accountService.findAdultAccounts(page, size);
Map<String, Object> response = new HashMap<>();
response.put("records", result.getRecords());
response.put("totalRow", result.getTotalRow());
response.put("totalPage", result.getTotalPage());
response.put("currentPage", page);
response.put("pageSize", size);
return response;
}

The frontend receives everything it needs to render pagination controls:

{
"records": [...],
"totalRow": 5,
"totalPage": 3,
"currentPage": 1,
"pageSize": 2
}

Common Mistakes I Avoided

Using 0-based page numbering: MyBatis-Flex uses 1-based numbering. The first page is 1, not 0.

Inconsistent filters between count and data queries: Since paginate() applies the same QueryWrapper to both count and data queries, I didn’t have this problem. But if I wrote separate queries, I’d need to ensure the WHERE clauses match exactly.

Not checking page bounds: If a user requests page 10 but there are only 3 pages, getRecords() returns an empty list. I handle this in the frontend by checking totalPage.

Summary

In this post, I showed how to implement pagination in MyBatis-Flex using the paginate() method. The Page<T> object provides both the current page’s records and metadata like getTotalRow() and getTotalPage(). Remember that page numbering starts at 1, not 0.

Final Words + More Resources

My intention with this article was to help others share my knowledge and experience. If you want to contact me, you can contact by email: Email me

Here are also the most important links from this article along with some further resources that will help you in this scope:

Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!

Comments