CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-com-baomidou--mybatis-plus-extension

MyBatis-Plus Extension module providing advanced features including service layers, Kotlin extensions, SQL parsers, caching mechanisms, and various interceptors for enhanced ORM functionality

Pending
Overview
Eval results
Files

pagination.mddocs/

Pagination

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.

Core Components

Page Class

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);
}

IPage Interface

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();
    }
}

OrderItem Class

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);
}

PageDTO Class

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
}

Database Dialects

MyBatis-Plus Extension supports multiple database dialects for optimal pagination SQL generation.

IDialect Interface

public interface IDialect {
    boolean willDoCount(IPage<?> page, List<MappedStatement> countMs);
    String buildPaginationSql(String originalSql, long offset, long limit);
}

Available Dialects

// 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;

Usage Examples

Basic Pagination

@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);

Pagination with Conditions

// 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());
}

Pagination with Ordering

// 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"));

Static Factory Methods

// 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);

Navigation Helpers

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);

Count Query Optimization

// 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

Lambda Query Chains with Pagination

// 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();

Chain Wrappers with Pagination

// Using chain wrappers
Page<User> page = ChainWrappers.queryChain(userMapper)
    .eq("department", "IT")
    .isNotNull("email")
    .orderByAsc("name")
    .page(new Page<>(1, 15));

Custom Count Query

// 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);

Map Results Pagination

// 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"));
}

Advanced Features

Custom Pagination Interceptor Configuration

@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;
    }
}

Custom Dialect

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;
    }
}

Performance Optimizations

// 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);

Infinite Scroll Pattern

// 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);

Error Handling

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);
}

Performance Considerations

  • Use setSearchCount(false) when total count is not needed
  • Enable count query optimizations with setOptimizeCountSql(true)
  • Set reasonable maxLimit to prevent memory issues
  • Consider cursor-based pagination for very large datasets
  • Use database-appropriate dialects for optimal SQL generation
  • Cache count results for expensive queries when possible

Install with Tessl CLI

npx tessl i tessl/maven-com-baomidou--mybatis-plus-extension

docs

active-record.md

condition-builders.md

index.md

kotlin-extensions.md

pagination.md

plugin-system.md

service-layer.md

static-utilities.md

tile.json