MyBatis-Plus is an enhanced toolkit for MyBatis providing CRUD operations, query wrappers, pagination, code generation, and Spring Boot integration.
—
MyBatis-Plus provides comprehensive pagination support with automatic count queries, flexible page configuration, and seamless integration with query wrappers for efficient large dataset handling.
Core pagination interface defining page structure and navigation methods.
/**
* Pagination interface
* @param <T> Record type
*/
public interface IPage<T> extends Serializable {
/**
* Get current page records
*/
List<T> getRecords();
/**
* Set current page records
*/
IPage<T> setRecords(List<T> records);
/**
* Get total record count
*/
long getTotal();
/**
* Set total record count
*/
IPage<T> setTotal(long total);
/**
* Get page size
*/
long getSize();
/**
* Set page size
*/
IPage<T> setSize(long size);
/**
* Get current page number (1-based)
*/
long getCurrent();
/**
* Set current page number
*/
IPage<T> setCurrent(long current);
/**
* Get total page count
*/
default long getPages() {
if (getSize() == 0) {
return 0L;
}
long pages = getTotal() / getSize();
if (getTotal() % getSize() != 0) {
pages++;
}
return pages;
}
/**
* Check if has next page
*/
default boolean hasNext() {
return getCurrent() < getPages();
}
/**
* Check if has previous page
*/
default boolean hasPrevious() {
return getCurrent() > 1;
}
/**
* Get orders for sorting
*/
default List<OrderItem> orders() {
return new ArrayList<>();
}
/**
* Whether to optimize count query
*/
default boolean optimizeCountSql() {
return true;
}
/**
* Whether to search count (perform count query)
*/
default boolean searchCount() {
return true;
}
/**
* Get count ID for optimization
*/
default String countId() {
return null;
}
/**
* Get max limit for single query
*/
default Long maxLimit() {
return null;
}
}Default pagination implementation with builder support and order configuration.
/**
* Default pagination implementation
* @param <T> Record type
*/
public class Page<T> implements IPage<T> {
/**
* Default constructor
*/
public Page();
/**
* Constructor with current page and page size
* @param current Current page number (1-based)
* @param size Page size
*/
public Page(long current, long size);
/**
* Constructor with current page, page size, and total
* @param current Current page number
* @param size Page size
* @param total Total record count
*/
public Page(long current, long size, long total);
/**
* Constructor with search count control
* @param current Current page number
* @param size Page size
* @param searchCount Whether to perform count query
*/
public Page(long current, long size, boolean searchCount);
/**
* Constructor with all parameters
* @param current Current page number
* @param size Page size
* @param total Total record count
* @param searchCount Whether to perform count query
*/
public Page(long current, long size, long total, boolean searchCount);
/**
* Add order item for sorting
* @param orderItem Order configuration
*/
public Page<T> addOrder(OrderItem orderItem);
/**
* Add multiple order items
* @param orderItems Order configurations
*/
public Page<T> addOrder(List<OrderItem> orderItems);
/**
* Set orders (replaces existing)
* @param orders New order configurations
*/
public Page<T> setOrders(List<OrderItem> orders);
/**
* Set whether to optimize count SQL
* @param optimizeCountSql Optimization flag
*/
public Page<T> setOptimizeCountSql(boolean optimizeCountSql);
/**
* Set whether to search count
* @param searchCount Search count flag
*/
public Page<T> setSearchCount(boolean searchCount);
/**
* Set count ID for optimization
* @param countId Count ID
*/
public Page<T> setCountId(String countId);
/**
* Set max limit for single query
* @param maxLimit Max limit
*/
public Page<T> setMaxLimit(Long maxLimit);
}Sorting configuration for pagination queries.
/**
* Order item for sorting configuration
*/
public class OrderItem implements Serializable {
/**
* Column name for sorting
*/
private String column;
/**
* Whether ascending (true) or descending (false)
*/
private boolean asc = true;
/**
* Constructor
*/
public OrderItem();
/**
* Constructor with column and direction
* @param column Column name
* @param asc Sort direction (true for ASC, false for DESC)
*/
public OrderItem(String column, boolean asc);
/**
* Static factory method for ascending order
* @param column Column name
* @return OrderItem for ascending sort
*/
public static OrderItem asc(String column);
/**
* Static factory method for descending order
* @param column Column name
* @return OrderItem for descending sort
*/
public static OrderItem desc(String column);
/**
* Static factory method for multiple ascending orders
* @param columns Column names
* @return List of OrderItems for ascending sorts
*/
public static List<OrderItem> ascs(String... columns);
/**
* Static factory method for multiple descending orders
* @param columns Column names
* @return List of OrderItems for descending sorts
*/
public static List<OrderItem> descs(String... columns);
// getters and setters...
}Basic Pagination:
// Simple pagination
Page<User> page = new Page<>(1, 10); // Page 1, 10 records per page
IPage<User> result = userService.page(page);
System.out.println("Total records: " + result.getTotal());
System.out.println("Total pages: " + result.getPages());
System.out.println("Current page: " + result.getCurrent());
System.out.println("Page size: " + result.getSize());
System.out.println("Records: " + result.getRecords().size());
System.out.println("Has next: " + result.hasNext());
System.out.println("Has previous: " + result.hasPrevious());Pagination with Query Conditions:
// Pagination with conditions
Page<User> page = new Page<>(1, 10);
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(User::getStatus, 1)
.like(User::getName, "John")
.orderByDesc(User::getCreateTime);
IPage<User> result = userService.page(page, wrapper);
// Or using mapper directly
IPage<User> result = userMapper.selectPage(page, wrapper);Pagination with Sorting:
// Using OrderItem for sorting
Page<User> page = new Page<>(1, 10);
page.addOrder(OrderItem.desc("create_time"))
.addOrder(OrderItem.asc("name"));
IPage<User> result = userService.page(page);
// Multiple orders at once
List<OrderItem> orders = Arrays.asList(
OrderItem.desc("create_time"),
OrderItem.asc("name"),
OrderItem.desc("id")
);
page.setOrders(orders);
// Factory methods for convenience
page.addOrder(OrderItem.descs("create_time", "update_time"))
.addOrder(OrderItem.ascs("name", "email"));Performance Optimization:
// Disable count query for better performance (when total not needed)
Page<User> page = new Page<>(1, 10, false); // searchCount = false
IPage<User> result = userService.page(page);
// result.getTotal() will be 0, but records are fetched normally
// Optimize count SQL
Page<User> page = new Page<>(1, 10);
page.setOptimizeCountSql(true); // Default is true
IPage<User> result = userService.page(page);
// Set count ID for complex queries
page.setCountId("customCountQuery");
// Set max limit to prevent large queries
page.setMaxLimit(1000L);Custom Pagination Queries:
// Custom mapper method with pagination
@Mapper
public interface UserMapper extends BaseMapper<User> {
@Select("SELECT u.*, d.name as dept_name FROM user u LEFT JOIN department d ON u.dept_id = d.id WHERE u.status = #{status}")
IPage<UserWithDept> selectUsersWithDept(IPage<UserWithDept> page, @Param("status") Integer status);
// XML-based custom query
IPage<User> selectActiveUsers(IPage<User> page, @Param("minAge") Integer minAge);
}
// Usage
Page<UserWithDept> page = new Page<>(1, 10);
IPage<UserWithDept> result = userMapper.selectUsersWithDept(page, 1);
Page<User> page = new Page<>(1, 10);
IPage<User> result = userMapper.selectActiveUsers(page, 18);Map-based Pagination:
// Paginate maps instead of entities
Page<Map<String, Object>> page = new Page<>(1, 10);
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.select(User::getId, User::getName, User::getEmail)
.eq(User::getStatus, 1);
IPage<Map<String, Object>> result = userService.pageMaps(page, wrapper);
// Or using mapper
IPage<Map<String, Object>> result = userMapper.selectMapsPage(page, wrapper);Service Layer Chain Pagination:
// Using service chain methods
Page<User> page = new Page<>(1, 10);
IPage<User> result = userService.lambdaQuery()
.eq(User::getStatus, 1)
.gt(User::getAge, 18)
.orderByDesc(User::getCreateTime)
.page(page);Pagination Helper Utilities:
// Utility class for common pagination operations
public class PageUtils {
public static <T> Page<T> buildPage(int current, int size) {
return new Page<>(current, size);
}
public static <T> Page<T> buildPageWithOrder(int current, int size, String orderBy, boolean asc) {
Page<T> page = new Page<>(current, size);
page.addOrder(new OrderItem(orderBy, asc));
return page;
}
public static <T> PageResult<T> toPageResult(IPage<T> page) {
PageResult<T> result = new PageResult<>();
result.setRecords(page.getRecords());
result.setTotal(page.getTotal());
result.setCurrent(page.getCurrent());
result.setSize(page.getSize());
result.setPages(page.getPages());
return result;
}
}
// Usage
Page<User> page = PageUtils.buildPageWithOrder(1, 10, "create_time", false);
IPage<User> result = userService.page(page);
PageResult<User> pageResult = PageUtils.toPageResult(result);Error Handling and Validation:
// Validate pagination parameters
public IPage<User> getUsers(int current, int size) {
// Validate parameters
if (current < 1) {
throw new IllegalArgumentException("Page number must be positive");
}
if (size < 1 || size > 100) {
throw new IllegalArgumentException("Page size must be between 1 and 100");
}
Page<User> page = new Page<>(current, size);
return userService.page(page);
}
// Handle pagination edge cases
public IPage<User> getUsersSafely(int current, int size) {
// Ensure valid parameters
current = Math.max(1, current);
size = Math.min(Math.max(1, size), 100);
Page<User> page = new Page<>(current, size);
IPage<User> result = userService.page(page);
// Check if current page exceeds total pages
if (result.getCurrent() > result.getPages() && result.getPages() > 0) {
// Redirect to last page
page.setCurrent(result.getPages());
result = userService.page(page);
}
return result;
}Performance Considerations:
searchCount = false when total count is not neededoptimizeCountSql = trueInstall with Tessl CLI
npx tessl i tessl/maven-com-baomidou--mybatis-plus