以下是添加了详细中文注释说明mybatis-plus-config.java.ftl 模板文件,涵盖每一行代码的设计意图、配置原理和企业级最佳实践:

package ${package.Config};

<#-- 导入 MyBatis-Plus 核心插件和拦截器 -->
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
<#-- 导入 MyBatis 原生 MetaObject,用于自动填充 -->
import org.apache.ibatis.reflection.MetaObject;
<#-- Spring 注解 -->
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.time.LocalDateTime;

/**
 * <p>
 * MyBatis-Plus 全局配置类
 * </p>
 *
 * <p>
 * 【核心作用】
 * 1. 注册 MyBatis-Plus 插件(如分页插件)
 * 2. 配置自动填充处理器(Auto Fill Handler)
 * 3. 统一数据库方言、逻辑删除等全局行为
 * </p>
 *
 * <p>
 * 【设计原则】
 * - 通过 Spring {@code @Configuration} 和 {@code @Bean} 声明式配置
 * - 插件链式注册,便于扩展(如添加多租户、性能分析插件)
 * - 自动填充逻辑集中管理,避免在每个 Entity 中重复
 * - 与 application.yml 配置互补,代码配置优先级更高
 * </p>
 *
 * <p>
 * 【注意事项】
 * - 此配置类会被 Spring 自动扫描并加载(需在 ComponentScan 范围内)
 * - 若同时存在 application.yml 中的 MP 配置,代码配置会覆盖 YAML 配置
 * - 分页插件必须在第一个位置(MyBatis-Plus 要求)
 * </p>
 *
 * @author ${author}
 * @since ${date}
 */
@Configuration
public class MybatisPlusConfig {

    /**
     * 注册 MyBatis-Plus 拦截器链
     *
     * <p>
     * 【拦截器链说明】
     * MyBatis-Plus 通过 {@link MybatisPlusInterceptor} 管理多个插件,
     * 插件按添加顺序执行(注意:分页插件必须放在第一位!)。
     * </p>
     *
     * <p>
     * 【当前注册的插件】
     * - {@link PaginationInnerInterceptor}:分页插件
     *   - 自动拦截 {@code selectPage} 等方法
     *   - 改写 SQL 添加 LIMIT/OFFSET
     *   - 支持多种数据库方言(MySQL、Oracle、PostgreSQL 等)
     * </p>
     *
     * <p>
     * 【扩展建议】
     * 可在此添加更多插件,例如:
     * - {@code BlockAttackInnerInterceptor}:防止全表更新/删除
     * - {@code TenantLineInnerInterceptor}:多租户插件
     * - {@code OptimisticLockerInnerInterceptor}:乐观锁插件
     * </p>
     *
     * @return 配置好的 MyBatis-Plus 拦截器
     */
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        // 创建拦截器实例
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        
        // 添加分页插件(必须放在第一位!)
        // DbType.MYSQL 表示使用 MySQL 方言(自动处理 LIMIT 语法)
        // 其他常用值:DbType.ORACLE, DbType.POSTGRE_SQL, DbType.SQL_SERVER
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        
        // 【扩展点】在此添加其他插件
        // interceptor.addInnerInterceptor(new BlockAttackInnerInterceptor());
        
        return interceptor;
    }

    /**
     * 注册自动填充处理器
     *
     * <p>
     * 【自动填充原理】
     * 当执行 INSERT/UPDATE 操作时,MyBatis-Plus 会回调此处理器,
     * 根据字段上的 {@code @TableField(fill = ...)} 注解自动填充值。
     * </p>
     *
     * <p>
     * 【填充策略说明】
     * - {@code FieldFill.INSERT}:仅插入时填充
     * - {@code FieldFill.UPDATE}:仅更新时填充
     * - {@code FieldFill.INSERT_UPDATE}:插入和更新时都填充
     * </p>
     *
     * <p>
     * 【当前填充逻辑】
     * 1. 插入时:
     *    - createTime = 当前时间
     *    - updateTime = 当前时间
     *    - deleted = 0(未删除)
     * 2. 更新时:
     *    - updateTime = 当前时间
     * </p>
     *
     * <p>
     * 【关键方法说明】
     * - {@code strictInsertFill}:严格插入填充(字段必须存在)
     * - {@code strictUpdateFill}:严格更新填充(字段必须存在)
     * - 使用 Lambda 表达式提供值(如 {@code LocalDateTime::now})
     * </p>
     *
     * <p>
     * 【与 Entity 的配合】
     * Entity 字段需添加注解,例如:
     * {@code @TableField(fill = FieldFill.INSERT)}
     * private LocalDateTime createTime;
     * </p>
     *
     * @return 自动填充处理器实例
     */
    @Bean
    public MetaObjectHandler metaObjectHandler() {
        return new MetaObjectHandler() {
            /**
             * 插入操作自动填充
             *
             * <p>
             * 【执行时机】
             * 在执行 {@code insert} 或 {@code insertBatch} 前触发
             * </p>
             *
             * <p>
             * 【填充字段】
             * - createTime:创建时间(仅插入时设置)
             * - updateTime:更新时间(插入时也设置为当前时间)
             * - deleted:逻辑删除标识(默认 0 表示未删除)
             * </p>
             *
             * @param metaObject MyBatis 元对象,包含实体信息
             */
            @Override
            public void insertFill(MetaObject metaObject) {
                // 填充 createTime 字段(仅当字段存在且未手动设置时)
                // LocalDateTime::now 是 Supplier<LocalDateTime>,提供当前时间
                this.strictInsertFill(metaObject, "createTime", LocalDateTime::now, LocalDateTime.class);
                // this.strictInsertFill(metaObject, BaseEntity::getCreateTime, LocalDateTime::now, LocalDateTime.class);
                
                // 填充 updateTime 字段(插入时也设为当前时间)
                this.strictInsertFill(metaObject, "updateTime", LocalDateTime::now, LocalDateTime.class);
                // this.strictInsertFill(metaObject, BaseEntity::getUpdateTime, LocalDateTime::now, LocalDateTime.class);
                
                // 填充 deleted 字段(默认 0 表示未删除)
                // 使用 Lambda 表达式提供默认值:() -> 0
                this.strictInsertFill(metaObject, "deleted", () -> 0, Integer.class);
                // this.strictInsertFill(metaObject, BaseEntity::getDeleted, () -> 0, Integer.class);
            }

            /**
             * 更新操作自动填充
             *
             * <p>
             * 【执行时机】
             * 在执行 {@code updateById} 或 {@code update} 前触发
             * </p>
             *
             * <p>
             * 【填充字段】
             * - updateTime:每次更新时刷新为当前时间
             * </p>
             *
             * @param metaObject MyBatis 元对象,包含实体信息
             */
            @Override
            public void updateFill(MetaObject metaObject) {
                // 仅填充 updateTime 字段
                this.strictUpdateFill(metaObject, "updateTime", LocalDateTime::now, LocalDateTime.class);
                // this.strictInsertFill(metaObject, BaseEntity::getUpdateTime, LocalDateTime::now, LocalDateTime.class);
            }
        };
    }
}

🔍 关键设计说明补充

1. 为什么分页插件必须放在第一位?
  • MyBatis-Plus 内部对插件执行顺序有严格要求
  • 分页插件需要最先解析 SQL,才能正确提取原始查询并改写
  • 若放在后面,可能被其他插件(如多租户)干扰,导致分页失效
2. strictInsertFill vs fillStrategy
  • strictInsertFill严格模式,仅当字段未设置值时才填充
  • 避免覆盖用户手动设置的值(如创建时指定特殊时间)
  • 推荐始终使用 strict* 方法,而非已废弃的 fillStrategy
3. 数据库方言(DbType)选择
数据库 DbType 常量 说明
MySQL DbType.MYSQL 默认使用
PostgreSQL DbType.POSTGRE_SQL 注意拼写
Oracle DbType.ORACLE 需额外配置
SQL Server DbType.SQL_SERVER -
达梦 DbType.DM 国产数据库
4. 自动填充的替代方案
  • 方案A(推荐):代码配置(本模板方式)— 灵活、类型安全

  • 方案B:YAML 配置 — 简单但不够灵活

    mybatis-plus:
      global-config:
        db-config:
          insert-strategy: not_null
          update-strategy: not_null
    
5. 生产环境增强建议
// 防止全表更新/删除(安全防护)
interceptor.addInnerInterceptor(new BlockAttackInnerInterceptor());

// 乐观锁插件(需 Entity 添加 @Version 注解)
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());

✅ 此模板文件是 MyBatis-Plus 应用的核心配置,正确配置后可:

  • 实现无侵入式分页
  • 自动管理创建/更新时间
  • 统一逻辑删除行为
  • 为后续扩展(多租户、审计等)奠定基础

可直接用于企业级项目,确保数据访问层的健壮性和可维护性。

Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐