MyBatis-Plus Extension module providing advanced features including service layers, Kotlin extensions, SQL parsers, caching mechanisms, and various interceptors for enhanced ORM functionality
—
MyBatis-Plus Extension provides comprehensive pagination support through the Page class and PaginationInnerInterceptor. The pagination system automatically handles count queries, limit/offset generation, and database-specific SQL dialect differences.
The main pagination container that implements IPage<T>.
public class Page<T> implements IPage<T> {
private static final long serialVersionUID = 8545996863226528798L;
// Core pagination fields
private List<T> records = Collections.emptyList();
private long total = 0;
private long size = 10;
private long current = 1;
private List<OrderItem> orders = new ArrayList<>();
private boolean optimizeCountSql = true;
private boolean optimizeJoinOfCountSql = true;
private boolean searchCount = true;
private boolean hitCount = false;
private String countId;
private Long maxLimit;
// Constructors
public Page();
public Page(long current, long size);
public Page(long current, long size, long total);
public Page(long current, long size, boolean searchCount);
public Page(long current, long size, long total, boolean searchCount);
// Static factory methods
public static <T> Page<T> of(long current, long size);
public static <T> Page<T> of(long current, long size, long total);
public static <T> Page<T> of(long current, long size, boolean searchCount);
// Data access methods
public List<T> getRecords();
public Page<T> setRecords(List<T> records);
public long getTotal();
public Page<T> setTotal(long total);
public long getSize();
public Page<T> setSize(long size);
public long getCurrent();
public Page<T> setCurrent(long current);
// Navigation methods
public long getPages();
public long offset();
public boolean hasPrevious();
public boolean hasNext();
// Ordering methods
public List<OrderItem> orders();
public Page<T> addOrder(OrderItem... items);
public Page<T> addOrder(List<OrderItem> items);
// Configuration methods
public boolean optimizeCountSql();
public Page<T> setOptimizeCountSql(boolean optimizeCountSql);
public boolean optimizeJoinOfCountSql();
public Page<T> setOptimizeJoinOfCountSql(boolean optimizeJoinOfCountSql);
public boolean searchCount();
public Page<T> setSearchCount(boolean searchCount);
public Long maxLimit();
public Page<T> setMaxLimit(Long maxLimit);
}The base pagination interface.
public interface IPage<T> extends Serializable {
List<T> getRecords();
IPage<T> setRecords(List<T> records);
long getTotal();
IPage<T> setTotal(long total);
long getSize();
IPage<T> setSize(long size);
long getCurrent();
IPage<T> setCurrent(long current);
// Computed properties
default long getPages() {
if (getSize() == 0) {
return 0L;
}
long pages = getTotal() / getSize();
if (getTotal() % getSize() != 0) {
pages++;
}
return pages;
}
default long offset() {
return getCurrent() > 0 ? (getCurrent() - 1) * getSize() : 0;
}
default boolean hasPrevious() {
return getCurrent() > 1;
}
default boolean hasNext() {
return getCurrent() < getPages();
}
}Represents ordering criteria for pagination queries.
public class OrderItem implements Serializable {
private String column;
private boolean asc = true;
public OrderItem();
public OrderItem(String column, boolean asc);
// Static factory methods
public static OrderItem asc(String column);
public static OrderItem desc(String column);
public static List<OrderItem> ascs(String... columns);
public static List<OrderItem> descs(String... columns);
// Getters and setters
public String getColumn();
public void setColumn(String column);
public boolean isAsc();
public void setAsc(boolean asc);
}Data Transfer Object for pagination parameters.
public class PageDTO<T> implements IPage<T> {
private List<T> records = Collections.emptyList();
private long total = 0;
private long size = 10;
private long current = 1;
// Implements IPage methods
}MyBatis-Plus Extension supports multiple database dialects for optimal pagination SQL generation.
public interface IDialect {
boolean willDoCount(IPage<?> page, List<MappedStatement> countMs);
String buildPaginationSql(String originalSql, long offset, long limit);
}// MySQL/MariaDB
public class MySqlDialect implements IDialect;
// PostgreSQL
public class PostgreDialect implements IDialect;
// Oracle
public class OracleDialect implements IDialect;
public class Oracle12cDialect implements IDialect;
// SQL Server
public class SQLServerDialect implements IDialect;
public class SQLServer2005Dialect implements IDialect;
// DB2
public class DB2Dialect implements IDialect;
// Other databases
public class InformixDialect implements IDialect;
public class SybaseDialect implements IDialect;
public class GBase8sDialect implements IDialect;
public class XCloudDialect implements IDialect;
public class TrinoDialect implements IDialect;@Autowired
private UserService userService;
// Create page parameters
Page<User> page = new Page<>(1, 10); // page 1, size 10
// Execute paginated query
Page<User> result = userService.page(page);
// Access results
List<User> users = result.getRecords();
long totalRecords = result.getTotal();
long totalPages = result.getPages();
long currentPage = result.getCurrent();
long pageSize = result.getSize();
System.out.println("Found " + totalRecords + " users");
System.out.println("Page " + currentPage + " of " + totalPages);// Create page with conditions
Page<User> page = new Page<>(1, 20);
QueryWrapper<User> queryWrapper = new QueryWrapper<User>()
.eq("active", true)
.like("name", "John");
Page<User> result = userService.page(page, queryWrapper);
// Process results
for (User user : result.getRecords()) {
System.out.println("User: " + user.getName());
}// Add ordering to pagination
Page<User> page = new Page<User>(1, 10)
.addOrder(OrderItem.desc("created_time"))
.addOrder(OrderItem.asc("name"));
Page<User> result = userService.page(page);
// Alternative: using static methods
Page<User> page2 = new Page<User>(1, 10)
.addOrder(OrderItem.descs("created_time", "updated_time"))
.addOrder(OrderItem.ascs("name", "email"));// Using static factory methods
Page<User> page1 = Page.of(1, 10);
Page<User> page2 = Page.of(2, 20, 100); // with total count
Page<User> page3 = Page.of(1, 15, false); // without count query
Page<User> result = userService.page(page1);Page<User> result = userService.page(new Page<>(2, 10));
// Check navigation state
if (result.hasPrevious()) {
System.out.println("Previous page available");
}
if (result.hasNext()) {
System.out.println("Next page available");
}
// Calculate offset
long offset = result.offset(); // (current - 1) * size
System.out.println("Offset: " + offset);// Disable count query for performance
Page<User> page = new Page<User>(1, 10)
.setSearchCount(false); // No total count query
Page<User> result = userService.page(page);
// result.getTotal() will be 0
// Optimize count SQL
Page<User> optimizedPage = new Page<User>(1, 10)
.setOptimizeCountSql(true) // Remove unnecessary ORDER BY in count
.setOptimizeJoinOfCountSql(true); // Optimize JOIN queries in count
// Set max limit to prevent large page sizes
Page<User> limitedPage = new Page<User>(1, 1000)
.setMaxLimit(500L); // Will be capped at 500// Using lambda query chains
Page<User> page = userService.lambdaQuery()
.eq(User::getActive, true)
.gt(User::getAge, 18)
.orderByDesc(User::getCreatedTime)
.page(new Page<>(1, 10));
List<User> users = page.getRecords();// Using chain wrappers
Page<User> page = ChainWrappers.queryChain(userMapper)
.eq("department", "IT")
.isNotNull("email")
.orderByAsc("name")
.page(new Page<>(1, 15));// Use custom count query ID
Page<User> page = new Page<User>(1, 10)
.setCountId("selectUserCountCustom"); // Custom count method
Page<User> result = userService.page(page, queryWrapper);// Paginate map results
Page<Map<String, Object>> mapPage = new Page<>(1, 10);
Page<Map<String, Object>> result = userService.pageMaps(mapPage,
new QueryWrapper<User>().select("id", "name", "email"));
for (Map<String, Object> record : result.getRecords()) {
System.out.println("ID: " + record.get("id"));
System.out.println("Name: " + record.get("name"));
}@Configuration
public class PaginationConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
PaginationInnerInterceptor paginationInterceptor =
new PaginationInnerInterceptor(DbType.MYSQL);
// Configure options
paginationInterceptor.setMaxLimit(1000L); // Max page size
paginationInterceptor.setOverflow(false); // Handle page overflow
paginationInterceptor.setOptimizeJoin(true); // Optimize JOIN in count
interceptor.addInnerInterceptor(paginationInterceptor);
return interceptor;
}
}public class CustomDialect implements IDialect {
@Override
public boolean willDoCount(IPage<?> page, List<MappedStatement> countMs) {
return page.searchCount();
}
@Override
public String buildPaginationSql(String originalSql, long offset, long limit) {
// Custom pagination SQL generation
return originalSql + " LIMIT " + offset + ", " + limit;
}
}
// Register custom dialect
@Configuration
public class CustomDialectConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
PaginationInnerInterceptor paginationInterceptor =
new PaginationInnerInterceptor();
paginationInterceptor.setDialect(new CustomDialect());
interceptor.addInnerInterceptor(paginationInterceptor);
return interceptor;
}
}// For large datasets, disable count query on subsequent pages
Page<User> firstPage = new Page<>(1, 50);
Page<User> result1 = userService.page(firstPage);
// Subsequent pages without count
Page<User> secondPage = new Page<User>(2, 50)
.setSearchCount(false)
.setTotal(result1.getTotal()); // Use previous total
Page<User> result2 = userService.page(secondPage);// Implementing infinite scroll
public Page<User> getNextPage(Long lastId, int size) {
Page<User> page = new Page<User>(1, size)
.setSearchCount(false); // No count needed for infinite scroll
QueryWrapper<User> wrapper = new QueryWrapper<User>()
.gt("id", lastId) // Cursor-based pagination
.orderByAsc("id");
return userService.page(page, wrapper);
}
// Usage
Page<User> firstBatch = getNextPage(0L, 20);
Long lastId = firstBatch.getRecords().get(firstBatch.getRecords().size() - 1).getId();
Page<User> nextBatch = getNextPage(lastId, 20);try {
Page<User> page = new Page<>(100, 50); // Potentially invalid page
Page<User> result = userService.page(page);
if (result.getRecords().isEmpty() && result.getCurrent() > 1) {
// Handle empty page - might want to redirect to last valid page
long lastPage = result.getPages();
page.setCurrent(lastPage);
result = userService.page(page);
}
} catch (Exception e) {
// Handle pagination errors
log.error("Pagination error", e);
}setSearchCount(false) when total count is not neededsetOptimizeCountSql(true)maxLimit to prevent memory issuesInstall with Tessl CLI
npx tessl i tessl/maven-com-baomidou--mybatis-plus-extension