CtrlK
BlogDocsLog inGet started
Tessl Logo

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

MyBatis-Plus is an enhanced toolkit for MyBatis providing CRUD operations, query wrappers, pagination, code generation, and Spring Boot integration.

Pending
Overview
Eval results
Files

plugins.mddocs/

Plugins and Interceptors

MyBatis-Plus provides an extensible plugin system for adding cross-cutting functionality like pagination, optimistic locking, tenant isolation, SQL security, and performance monitoring through interceptors.

Capabilities

MybatisPlusInterceptor

Main interceptor that manages inner interceptors for different functionalities.

/**
 * MyBatis-Plus main interceptor
 */
public class MybatisPlusInterceptor implements Interceptor {
    
    /**
     * Add inner interceptor
     * @param innerInterceptor Inner interceptor to add
     */
    public void addInnerInterceptor(InnerInterceptor innerInterceptor);
    
    /**
     * Get all inner interceptors
     * @return List of inner interceptors
     */
    public List<InnerInterceptor> getInterceptors();
    
    /**
     * Set properties for configuration
     * @param properties Configuration properties
     */
    public void setProperties(Properties properties);
}

InnerInterceptor Interface

Base interface for all inner interceptors.

/**
 * Inner interceptor interface
 */
public interface InnerInterceptor {
    
    /**
     * Check if query should be intercepted
     * @param executor MyBatis executor
     * @param ms Mapped statement
     * @param parameter Query parameters
     * @param rowBounds Row bounds
     * @param resultHandler Result handler
     * @param boundSql Bound SQL
     * @return true to intercept
     */
    default boolean willDoQuery(Executor executor, MappedStatement ms, Object parameter, 
                               RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
        return true;
    }
    
    /**
     * Before query execution
     * @param executor MyBatis executor
     * @param ms Mapped statement
     * @param parameter Query parameters
     * @param rowBounds Row bounds
     * @param resultHandler Result handler
     * @param boundSql Bound SQL
     */
    default void beforeQuery(Executor executor, MappedStatement ms, Object parameter,
                           RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
        // Implementation specific
    }
    
    /**
     * Check if update should be intercepted
     * @param executor MyBatis executor
     * @param ms Mapped statement
     * @param parameter Update parameters
     * @return true to intercept
     */
    default boolean willDoUpdate(Executor executor, MappedStatement ms, Object parameter) throws SQLException {
        return true;
    }
    
    /**
     * Before update execution
     * @param executor MyBatis executor
     * @param ms Mapped statement
     * @param parameter Update parameters
     */
    default void beforeUpdate(Executor executor, MappedStatement ms, Object parameter) throws SQLException {
        // Implementation specific
    }
}

Core Interceptors

PaginationInnerInterceptor

Automatic pagination support with count query optimization.

/**
 * Pagination interceptor
 */
public class PaginationInnerInterceptor implements InnerInterceptor {
    
    /**
     * Default constructor
     */
    public PaginationInnerInterceptor();
    
    /**
     * Constructor with database type
     * @param dbType Database type
     */
    public PaginationInnerInterceptor(DbType dbType);
    
    /**
     * Set maximum limit for single query
     * @param maxLimit Maximum limit
     */
    public void setMaxLimit(Long maxLimit);
    
    /**
     * Set overflow handling
     * @param overflow Whether to handle page overflow
     */
    public void setOverflow(boolean overflow);
    
    /**
     * Set whether to optimize count query
     * @param optimizeCountSql Optimization flag
     */
    public void setOptimizeCountSql(boolean optimizeCountSql);
}

OptimisticLockerInnerInterceptor

Optimistic locking support using version fields.

/**
 * Optimistic locking interceptor
 */
public class OptimisticLockerInnerInterceptor implements InnerInterceptor {
    
    /**
     * Default constructor
     */
    public OptimisticLockerInnerInterceptor();
}

TenantLineInnerInterceptor

Multi-tenant support with automatic tenant line filtering.

/**
 * Tenant line interceptor for multi-tenant applications
 */
public class TenantLineInnerInterceptor implements InnerInterceptor {
    
    /**
     * Constructor with tenant line handler
     * @param tenantLineHandler Tenant line handler
     */
    public TenantLineInnerInterceptor(TenantLineHandler tenantLineHandler);
    
    /**
     * Set tenant line handler
     * @param tenantLineHandler Tenant line handler
     */
    public void setTenantLineHandler(TenantLineHandler tenantLineHandler);
}

/**
 * Tenant line handler interface
 */
public interface TenantLineHandler {
    
    /**
     * Get tenant ID
     * @return Current tenant ID
     */
    Expression getTenantId();
    
    /**
     * Get tenant ID column name
     * @return Tenant ID column name
     */
    String getTenantIdColumn();
    
    /**
     * Check if table should ignore tenant filtering
     * @param tableName Table name
     * @return true to ignore tenant filtering
     */
    default boolean ignoreTable(String tableName) {
        return false;
    }
    
    /**
     * Check if INSERT should ignore tenant column
     * @param columns Insert columns
     * @param tenantIdColumn Tenant ID column
     * @return true to ignore
     */
    default boolean ignoreInsert(List<Column> columns, String tenantIdColumn) {
        return false;
    }
}

DynamicTableNameInnerInterceptor

Dynamic table name support for table sharding scenarios.

/**
 * Dynamic table name interceptor
 */
public class DynamicTableNameInnerInterceptor implements InnerInterceptor {
    
    /**
     * Constructor with table name handler
     * @param tableNameHandler Table name handler
     */
    public DynamicTableNameInnerInterceptor(TableNameHandler tableNameHandler);
    
    /**
     * Set table name handler
     * @param tableNameHandler Table name handler
     */
    public void setTableNameHandler(TableNameHandler tableNameHandler);
}

/**
 * Table name handler interface
 */
public interface TableNameHandler {
    
    /**
     * Dynamic table name replacement
     * @param sql Original SQL
     * @param tableName Original table name
     * @return New table name
     */
    String dynamicTableName(String sql, String tableName);
}

BlockAttackInnerInterceptor

Protection against dangerous SQL operations.

/**
 * Block attack interceptor to prevent dangerous operations
 */
public class BlockAttackInnerInterceptor implements InnerInterceptor {
    
    /**
     * Default constructor
     */
    public BlockAttackInnerInterceptor();
}

IllegalSQLInnerInterceptor

Detection and blocking of illegal SQL patterns.

/**
 * Illegal SQL interceptor
 */
public class IllegalSQLInnerInterceptor implements InnerInterceptor {
    
    /**
     * Default constructor
     */
    public IllegalSQLInnerInterceptor();
}

DataChangeRecorderInnerInterceptor (MyBatis-Plus 3.5.7+)

Audit interceptor for tracking data changes with before/after values, user information, and timestamps.

/**
 * Data change recording interceptor for audit trails
 */
public class DataChangeRecorderInnerInterceptor implements InnerInterceptor {
    
    /**
     * Default constructor
     */
    public DataChangeRecorderInnerInterceptor();
    
    /**
     * Constructor with custom data change handler
     * @param dataChangeHandler Custom handler for processing data changes
     */
    public DataChangeRecorderInnerInterceptor(DataChangeHandler dataChangeHandler);
    
    /**
     * Set custom data change handler
     * @param dataChangeHandler Handler for processing data changes
     */
    public void setDataChangeHandler(DataChangeHandler dataChangeHandler);
}

/**
 * Handler interface for processing data changes
 */
public interface DataChangeHandler {
    
    /**
     * Handle data change event
     * @param changeRecord Data change record containing details
     */
    void handleDataChange(DataChangeRecord changeRecord);
}

/**
 * Data change record containing audit information
 */
public class DataChangeRecord {
    private String tableName;
    private String operation; // INSERT, UPDATE, DELETE
    private Object primaryKey;
    private Map<String, Object> beforeData;
    private Map<String, Object> afterData;
    private String userId;
    private LocalDateTime changeTime;
    private String changeReason;
    
    // getters and setters...
}

Usage Examples

Basic Interceptor Configuration:

@Configuration
public class MybatisPlusConfig {
    
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        
        // Add pagination interceptor
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        
        // Add optimistic locking interceptor
        interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
        
        // Add block attack interceptor
        interceptor.addInnerInterceptor(new BlockAttackInnerInterceptor());
        
        return interceptor;
    }
}

Pagination Configuration:

@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
    MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
    
    PaginationInnerInterceptor paginationInterceptor = new PaginationInnerInterceptor(DbType.MYSQL);
    
    // Set maximum limit to prevent large queries
    paginationInterceptor.setMaxLimit(1000L);
    
    // Enable overflow handling (redirect to first page if page exceeds total)
    paginationInterceptor.setOverflow(true);
    
    // Optimize count queries
    paginationInterceptor.setOptimizeCountSql(true);
    
    interceptor.addInnerInterceptor(paginationInterceptor);
    
    return interceptor;
}

Multi-Tenant Configuration:

@Component
public class CustomTenantLineHandler implements TenantLineHandler {
    
    @Override
    public Expression getTenantId() {
        // Get current tenant ID from security context, session, etc.
        String tenantId = TenantContextHolder.getCurrentTenantId();
        return new LongValue(Long.parseLong(tenantId));
    }
    
    @Override
    public String getTenantIdColumn() {
        return "tenant_id";
    }
    
    @Override
    public boolean ignoreTable(String tableName) {
        // Ignore tenant filtering for system tables
        return Arrays.asList("sys_config", "sys_dict", "sys_log").contains(tableName);
    }
    
    @Override
    public boolean ignoreInsert(List<Column> columns, String tenantIdColumn) {
        // Check if tenant column already exists in INSERT
        return columns.stream().anyMatch(column -> 
            column.getColumnName().equalsIgnoreCase(tenantIdColumn));
    }
}

@Configuration
public class MybatisPlusConfig {
    
    @Autowired
    private CustomTenantLineHandler tenantLineHandler;
    
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        
        // Add tenant line interceptor
        interceptor.addInnerInterceptor(new TenantLineInnerInterceptor(tenantLineHandler));
        
        return interceptor;
    }
}

Dynamic Table Name Configuration:

@Component
public class CustomTableNameHandler implements TableNameHandler {
    
    @Override
    public String dynamicTableName(String sql, String tableName) {
        // Table sharding by date
        if (\"user_log\".equals(tableName)) {\n            String datePrefix = LocalDate.now().format(DateTimeFormatter.ofPattern(\"yyyyMM\"));\n            return tableName + \"_\" + datePrefix; // user_log_202312\n        }\n        \n        // Table sharding by tenant\n        if (\"order\".equals(tableName)) {\n            String tenantId = TenantContextHolder.getCurrentTenantId();\n            return tableName + \"_\" + tenantId; // order_tenant_001\n        }\n        \n        return tableName;\n    }\n}\n\n@Configuration\npublic class MybatisPlusConfig {\n    \n    @Autowired\n    private CustomTableNameHandler tableNameHandler;\n    \n    @Bean\n    public MybatisPlusInterceptor mybatisPlusInterceptor() {\n        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();\n        \n        // Add dynamic table name interceptor\n        interceptor.addInnerInterceptor(new DynamicTableNameInnerInterceptor(tableNameHandler));\n        \n        return interceptor;\n    }\n}"
     },
     {
       "old_string": "",
       "new_string": "**Optimistic Locking Usage:**\n\n```java\n// Entity with version field\n@TableName(\"product\")\npublic class Product {\n    @TableId(type = IdType.AUTO)\n    private Long id;\n    \n    private String name;\n    private BigDecimal price;\n    \n    @Version // Optimistic locking version field\n    private Integer version;\n    \n    // getters and setters...\n}\n\n// Service method with optimistic locking\n@Service\npublic class ProductService {\n    \n    @Autowired\n    private ProductMapper productMapper;\n    \n    public boolean updateProductPrice(Long productId, BigDecimal newPrice) {\n        // Get current product with version\n        Product product = productMapper.selectById(productId);\n        if (product == null) {\n            return false;\n        }\n        \n        // Update price\n        product.setPrice(newPrice);\n        \n        // Update will automatically check version and increment it\n        int result = productMapper.updateById(product);\n        \n        // result = 0 means version conflict (concurrent update)\n        return result > 0;\n    }\n}\n```\n\n**Custom Interceptor Development:**\n\n```java\n@Component\npublic class SqlLoggingInterceptor implements InnerInterceptor {\n    \n    private static final Logger logger = LoggerFactory.getLogger(SqlLoggingInterceptor.class);\n    \n    @Override\n    public void beforeQuery(Executor executor, MappedStatement ms, Object parameter,\n                           RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {\n        logSqlExecution(\"QUERY\", ms.getId(), boundSql.getSql(), parameter);\n    }\n    \n    @Override\n    public void beforeUpdate(Executor executor, MappedStatement ms, Object parameter) {\n        BoundSql boundSql = ms.getBoundSql(parameter);\n        logSqlExecution(\"UPDATE\", ms.getId(), boundSql.getSql(), parameter);\n    }\n    \n    private void logSqlExecution(String type, String mapperId, String sql, Object parameter) {\n        if (logger.isDebugEnabled()) {\n            logger.debug(\"[{}] Mapper: {}\", type, mapperId);\n            logger.debug(\"[{}] SQL: {}\", type, sql.replaceAll(\"\\\\s+\", \" \").trim());\n            logger.debug(\"[{}] Parameters: {}\", type, parameter);\n        }\n    }\n}\n\n// Register custom interceptor\n@Configuration\npublic class MybatisPlusConfig {\n    \n    @Autowired\n    private SqlLoggingInterceptor sqlLoggingInterceptor;\n    \n    @Bean\n    public MybatisPlusInterceptor mybatisPlusInterceptor() {\n        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();\n        \n        // Add custom logging interceptor\n        interceptor.addInnerInterceptor(sqlLoggingInterceptor);\n        \n        // Add other interceptors...\n        \n        return interceptor;\n    }\n}\n```\n\n**Performance Monitoring Interceptor:**\n\n```java\n@Component\npublic class PerformanceMonitorInterceptor implements InnerInterceptor {\n    \n    private static final Logger logger = LoggerFactory.getLogger(PerformanceMonitorInterceptor.class);\n    private final ThreadLocal<Long> startTime = new ThreadLocal<>();\n    \n    @Override\n    public void beforeQuery(Executor executor, MappedStatement ms, Object parameter,\n                           RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {\n        startTime.set(System.currentTimeMillis());\n    }\n    \n    @Override\n    public void beforeUpdate(Executor executor, MappedStatement ms, Object parameter) {\n        startTime.set(System.currentTimeMillis());\n    }\n    \n    // Note: For complete implementation, you'd need to implement Interceptor interface directly\n    // This is a simplified example showing the concept\n    \n    public void afterExecution(String mapperId) {\n        Long start = startTime.get();\n        if (start != null) {\n            long duration = System.currentTimeMillis() - start;\n            if (duration > 1000) { // Log slow queries (> 1 second)\n                logger.warn(\"Slow query detected: {} took {}ms\", mapperId, duration);\n            }\n            startTime.remove();\n        }\n    }\n}\n```\n\n**Data Permission Interceptor:**\n\n```java\n@Component\npublic class DataPermissionInterceptor implements InnerInterceptor {\n    \n    @Override\n    public void beforeQuery(Executor executor, MappedStatement ms, Object parameter,\n                           RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {\n        \n        // Check if current user has permission to access data\n        String userId = SecurityContextHolder.getCurrentUserId();\n        String userRole = SecurityContextHolder.getCurrentUserRole();\n        \n        // Apply data filtering based on user permissions\n        if (!\"ADMIN\".equals(userRole)) {\n            // For non-admin users, add WHERE condition to limit data access\n            String originalSql = boundSql.getSql();\n            String permissionSql = addDataPermissionCondition(originalSql, userId, ms.getId());\n            \n            // Modify the bound SQL (implementation would require reflection)\n            // This is a conceptual example\n        }\n    }\n    \n    private String addDataPermissionCondition(String originalSql, String userId, String mapperId) {\n        // Add conditions based on business rules\n        if (mapperId.contains(\"selectUser\")) {\n            return originalSql + \" AND (created_by = '\" + userId + \"' OR assigned_to = '\" + userId + \"')\";\n        }\n        return originalSql;\n    }\n}\n```\n\n**Interceptor Order and Priority:**\n\n```java\n@Configuration\npublic class MybatisPlusConfig {\n    \n    @Bean\n    public MybatisPlusInterceptor mybatisPlusInterceptor() {\n        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();\n        \n        // Order matters! Interceptors are executed in the order they are added\n        \n        // 1. Multi-tenant filtering (should be first for security)\n        interceptor.addInnerInterceptor(new TenantLineInnerInterceptor(tenantLineHandler));\n        \n        // 2. Data permission filtering\n        interceptor.addInnerInterceptor(new DataPermissionInterceptor());\n        \n        // 3. Dynamic table name (for sharding)\n        interceptor.addInnerInterceptor(new DynamicTableNameInnerInterceptor(tableNameHandler));\n        \n        // 4. Pagination (should be after filtering interceptors)\n        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));\n        \n        // 5. Optimistic locking (for update operations)\n        interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());\n        \n        // 6. Security interceptors (should be after business logic)\n        interceptor.addInnerInterceptor(new BlockAttackInnerInterceptor());\n        interceptor.addInnerInterceptor(new IllegalSQLInnerInterceptor());\n        \n        // 7. Monitoring and logging (should be last)\n        interceptor.addInnerInterceptor(new PerformanceMonitorInterceptor());\n        \n        return interceptor;\n    }\n}\n```\n\n**Conditional Interceptor Configuration:**\n\n```java\n@Configuration\npublic class MybatisPlusConfig {\n    \n    @Value(\"${mybatis-plus.interceptor.pagination.enabled:true}\")\n    private boolean paginationEnabled;\n    \n    @Value(\"${mybatis-plus.interceptor.tenant.enabled:false}\")\n    private boolean tenantEnabled;\n    \n    @Bean\n    public MybatisPlusInterceptor mybatisPlusInterceptor() {\n        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();\n        \n        // Conditionally add interceptors based on configuration\n        if (tenantEnabled) {\n            interceptor.addInnerInterceptor(new TenantLineInnerInterceptor(tenantLineHandler));\n        }\n        \n        if (paginationEnabled) {\n            interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));\n        }\n        \n        // Always add security interceptors in production\n        if (isProductionEnvironment()) {\n            interceptor.addInnerInterceptor(new BlockAttackInnerInterceptor());\n            interceptor.addInnerInterceptor(new IllegalSQLInnerInterceptor());\n        }\n        \n        return interceptor;\n    }\n    \n    private boolean isProductionEnvironment() {\n        return \"production\".equals(System.getProperty(\"spring.profiles.active\"));\n    }\n}\n```\n\nThis plugin system provides powerful extensibility for adding cross-cutting concerns to MyBatis-Plus operations while maintaining clean separation of business logic and infrastructure concerns."
     }]
    }
]

Install with Tessl CLI

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

docs

active-record.md

annotations.md

code-generation.md

core-crud.md

index.md

pagination.md

plugins.md

query-building.md

service-layer.md

spring-boot.md

tile.json