一、核心概念

全局字段填充指在数据操作(插入/更新)时自动填充实体类中的特定字段(如 createTime, updateTime, operator 等)。MyBatis-Plus 通过 MetaObjectHandler 接口实现该功能。

核心接口

public interface MetaObjectHandler {
    void insertFill(MetaObject metaObject); // 插入时填充
    void updateFill(MetaObject metaObject); // 更新时填充
}

二、详细操作步骤

步骤 1:实体类添加注解

public class User {
    // 插入时填充
    @TableField(fill = FieldFill.INSERT)
    private LocalDateTime createTime;
    
    // 插入和更新时填充
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private LocalDateTime updateTime;
    
    // 插入时填充操作人
    @TableField(fill = FieldFill.INSERT)
    private String creator;
}

步骤 2:实现 MetaObjectHandler

@Component
public class MyMetaObjectHandler implements MetaObjectHandler {

    @Override
    public void insertFill(MetaObject metaObject) {
        // 方式1:严格匹配字段类型 (推荐)
        this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now());
        
        // 方式2:通用填充 (需注意类型)
        this.setFieldValByName("creator", getCurrentUsername(), metaObject);
    }

    @Override
    public void updateFill(MetaObject metaObject) {
        // 禁止覆盖已存在的值
        this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
    }
    
    private String getCurrentUsername() {
        // 从安全上下文获取当前用户
        return "admin"; // 示例
    }
}

步骤 3:配置生效(Spring Boot 自动注册)


三、常见错误与解决方案

错误现象 原因分析 解决方案
填充字段值为 null 字段名拼写错误或未加 @TableField 注解 检查字段名和注解
更新时未填充 未设置 FieldFill.UPDATEFieldFill.INSERT_UPDATE 修正注解填充策略
时间值早于当前时间 服务器时区设置错误 统一使用 UTCLocalDateTime.now()
批量插入不生效 需使用 MP 的 saveBatch() 方法 避免手动循环插入

四、关键注意事项

  1. 类型严格匹配
    使用 strictInsertFill/strictUpdateFill 时需确保字段类型与参数类型一致:

    // ✅ 正确
    this.strictInsertFill(metaObject, "price", BigDecimal.class, new BigDecimal("99.99"));
    
    // ❌ 错误:类型不匹配
    this.strictInsertFill(metaObject, "price", Double.class, 99.99);
    
  2. 避免覆盖已有值
    默认行为不覆盖已设置的值,需显式调用:

    // 强制覆盖
    metaObject.setValue("updateBy", "admin");
    
  3. 多数据源场景
    每个数据源需独立配置 MetaObjectHandler

    @Bean
    public MetaObjectHandler metaObjectHandler(@Qualifier("ds1") DataSource dataSource) {
        return new Ds1MetaObjectHandler();
    }
    

五、性能优化技巧

  1. 减少反射调用
    使用 strictFill 方法族替代通用 setFieldValByName

    // 性能更优 (缓存了字段的 setter 方法)
    this.strictInsertFill(metaObject, "createTime", LocalDateTime::now, LocalDateTime.class);
    
  2. 避免复杂逻辑
    不在 insertFill/updateFill 中执行数据库查询等耗时操作。

  3. 批量操作优化
    使用 MP 的 saveBatch() 方法,内部优化了填充逻辑。


六、最佳实践

场景 1:审计字段自动填充

public class AuditMetaObjectHandler extends MyMetaObjectHandler {
    @Override
    public void insertFill(MetaObject metaObject) {
        String user = getCurrentUser();
        this.strictInsertFill(metaObject, "createBy", String.class, user);
        this.strictInsertFill(metaObject, "updateBy", String.class, user);
    }
}

场景 2:逻辑删除填充

public class Product {
    @TableField(fill = FieldFill.INSERT)
    @TableLogic(value = "0", delval = "1")
    private Integer deleted; // 默认未删除
}

动态值获取技巧

@Override
public void updateFill(MetaObject metaObject) {
    // 从线程安全容器动态取值
    this.setFieldValByName("updater", RequestContextHolder.getCurrentUser(), metaObject);
}

七、补充:填充策略对比

策略 适用场景 性能 安全性
strictInsertFill 精确类型匹配 ★★★
setFieldValByName 动态字段名 ★★
直接修改 MetaObject 需要强制覆盖

建议:90% 场景使用 strictXxxFill 方法,兼顾类型安全和性能。


通过以上详解,可快速掌握 MyBatis-Plus 全局字段填充的核心要点,避免常见陷阱,并应用于实际项目中实现高效、安全的自动填充功能。重点注意字段类型匹配批量操作支持多数据源适配三大核心问题。