5 Ways to Implement Pagination in MyBatis: From Manual SQL to PageHelper

This article compares five MyBatis pagination techniques—including raw LIMIT SQL, RowBounds, custom window‑function queries, interceptor‑based pagination, and the PageHelper plugin—explaining their principles, code examples, performance trade‑offs, and ideal usage scenarios.

Lobster Programming
Lobster Programming
Lobster Programming
5 Ways to Implement Pagination in MyBatis: From Manual SQL to PageHelper

Pagination Principles

MyBatis supports two core pagination modes: logical (memory) pagination, which retrieves all matching rows from the database and then selects a subset in application memory, and physical pagination, which lets the database return only the required rows via SQL limits, offering far better performance.

Pagination modes diagram
Pagination modes diagram

Implementation Methods

1. Handwritten SQL with LIMIT

<select id="selectByPage" resultType="YourEntity">
  SELECT * FROM table
  LIMIT #{offset}, #{pageSize}
</select>

Pros: simple and direct, no extra configuration. Cons: tightly couples SQL to a specific database, requires repetitive LIMIT clauses, and increases maintenance when switching databases.

2. RowBounds

RowBounds rowBounds = new RowBounds(offset, limit);
List<YourEntity> list = sqlSession.selectList("userMapper.selectAll", null, rowBounds);

RowBounds still executes a full SELECT * query, loads all rows into memory, and then slices the result, which can cause OOM for large data sets. It is considered a logical pagination approach and is deprecated after MyBatis 3.4.0.

3. Custom SQL (e.g., Window Functions)

<select id="selectByPage" resultType="UserEntity">
  SELECT * FROM (
    SELECT name, ROW_NUMBER() OVER(ORDER BY className) AS rownum
    FROM user
  ) WHERE rownum BETWEEN #{startRow} AND #{endRow}
</select>

Use this when you need more complex pagination logic or want to leverage advanced database features such as window functions.

4. Interceptor‑Based Pagination

An interceptor can modify any incoming SQL statement before execution, appending a physical LIMIT clause (e.g., LIMIT 0,10). This provides high‑performance physical pagination while keeping pagination logic separate from business code, but requires writing a robust interceptor, raising the development barrier.

Interceptor diagram
Interceptor diagram

5. Mature Pagination Plugin (PageHelper)

PageHelper.startPage(pageNum, pageSize);
List<YourEntity> list = sqlSession.selectList("UserMapper.selectAll");

PageHelper injects the appropriate LIMIT clause via an internal interceptor, making pagination zero‑intrusive for business code, delivering excellent performance, and greatly improving developer productivity.

Conclusion

For most MyBatis projects, the recommended approach is to use a physical pagination plugin such as PageHelper because it is simple, efficient, and requires minimal code changes. RowBounds may be suitable for small‑scale quick prototypes, while handwritten SQL offers maximum flexibility for special requirements.

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.

BackendJavaSQLMyBatispagehelperRowBounds
Lobster Programming
Written by

Lobster Programming

Sharing insights on technical analysis and exchange, making life better through technology.

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.