源本科技 | 码上会

字段自动填充

2026/04/02
0
0

前言

MyBatis-Plus 自动填充用于统一处理数据库通用字段的赋值,无需在业务代码中手动设置,大幅简化开发:

  • 通用字段:创建人ID创建时间更新人ID更新时间

  • 核心优势:无侵入、统一维护、避免重复代码、保证数据规范


核心原理

  1. 基于 MetaObjectHandler 接口实现,MyBatis-Plus 拦截 insert/update 操作

  2. 通过 @TableField(fill = 策略) 标记需要填充的字段

  3. 自定义处理器实现插入 / 更新时的自动赋值逻辑

  4. 底层通过元对象 MetaObject 操作实体属性,无反射安全风险


数据库字段规范(推荐)

CREATE TABLE `user` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `name` varchar(50) DEFAULT NULL,
  `create_user_id` bigint DEFAULT NULL COMMENT '创建人ID',
  `create_time` datetime DEFAULT NULL COMMENT '创建时间',
  `update_user_id` bigint DEFAULT NULL COMMENT '更新人ID',
  `update_time` datetime DEFAULT NULL COMMENT '更新时间',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

标准使用步骤

企业级最佳实践

步骤 1

抽取通用基类(复用字段,推荐)

所有实体继承该基类,无需重复定义通用字段

import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import lombok.Data;
import java.time.LocalDateTime;

@Data
public class BaseEntity {
    // 插入时填充
    @TableField(fill = FieldFill.INSERT)
    private Long createUserId;

    @TableField(fill = FieldFill.INSERT)
    private LocalDateTime createTime;

    // 插入+更新时填充
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Long updateUserId;

    @TableField(fill = FieldFill.INSERT_UPDATE)
    private LocalDateTime updateTime;
}

步骤 2

实体类继承基类

import lombok.Data;
import lombok.EqualsAndHashCode;

@Data
@EqualsAndHashCode(callSuper = true)
public class User extends BaseEntity {
    private String name;
}

步骤 3

实现自动填充处理器(核心)

修正原代码错误:更新操作必须用 strictUpdateFill,支持动态获取登录用户 ID(非写死)

import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;

/**
 * 自动填充配置类,必须交给Spring管理
 */
@Slf4j
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {

    /**
     * 插入数据时填充
     */
    @Override
    public void insertFill(MetaObject metaObject) {
        log.info("【自动填充】执行插入填充...");
        // 1. 填充创建/更新人(实际业务从上下文获取登录用户ID)
        Long userId = getCurrentUserId();
        // 2. 严格填充:字段类型匹配、注解策略匹配才赋值
        this.strictInsertFill(metaObject, "createUserId", Long.class, userId);
        this.strictInsertFill(metaObject, "updateUserId", Long.class, userId);
        // 3. 填充时间
        this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now());
        this.strictInsertFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
    }

    /**
     * 更新数据时填充
     */
    @Override
    public void updateFill(MetaObject metaObject) {
        log.info("【自动填充】执行更新填充...");
        Long userId = getCurrentUserId();
        // 更新操作必须使用 strictUpdateFill
        this.strictUpdateFill(metaObject, "updateUserId", Long.class, userId);
        this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
    }

    /**
     * 模拟:获取当前登录用户ID
     * 实际项目:从SpringSecurity、ThreadLocal、Token中获取
     */
    private Long getCurrentUserId() {
        // 示例:默认1L,替换为真实业务逻辑
        return 1L;
    }
}

核心 API

严格填充方法(推荐)

// 插入严格填充
this.strictInsertFill(元对象, 字段名, 字段类型, 填充值);
// 更新严格填充
this.strictUpdateFill(元对象, 字段名, 字段类型, 填充值);

规则

  • 必须匹配 @TableField(fill = 策略)

  • 必须匹配字段类型

  • 字段已有值 → 不覆盖

  • 填充值为 null → 不填充

通用填充方法

无策略校验

无视 FieldFill 注解,直接赋值,适用于自定义场景:

this.fillStrategy(metaObject, "updateTime", LocalDateTime.now());

强制覆盖填充

高级

无视原有值,强制赋值

this.setFieldValByName("updateTime", LocalDateTime.now(), metaObject);

FieldFill 填充策略枚举

public enum FieldFill {
    DEFAULT,        // 默认:不填充
    INSERT,         // 仅插入时填充
    UPDATE,         // 仅更新时填充
    INSERT_UPDATE   // 插入+更新时都填充(更新人/时间专用)
}

自动填充场景

生效场景

  1. baseMapper.insert(entity)

  2. baseMapper.updateById(entity)

  3. baseMapper.update(entity, wrapper)

  4. 批量插入 / 更新(MP 批量方法)

  5. 方法参数实体名为 et(默认规则)

失效场景(高频)

  1. 更新时 entity 为 nullupdate(null, wrapper) → 不填充

  2. 自定义 Mapper 方法参数名不是 et

    // 失效
    updateXXX(@Param("user") User user);
    // 生效
    updateXXX(@Param("et") User user);
  3. 处理器未加 @Component(未被 Spring 管理)

  4. 字段类型不匹配(如实体用 String,填充用 LocalDateTime)


测试用例

@SpringBootTest
public class AutoFillTest {
    @Autowired
    private UserMapper userMapper;

    @Test
    public void testInsert() {
        User user = new User();
        user.setName("测试用户");
        // 自动填充:createUserId、createTime、updateUserId、updateTime
        userMapper.insert(user);
    }

    @Test
    public void testUpdate() {
        User user = userMapper.selectById(1);
        user.setName("修改后的名字");
        // 自动填充:updateUserId、updateTime
        userMapper.updateById(user);
    }
}

总结

  1. 核心组件@TableField(fill) 标记字段 + MetaObjectHandler 实现填充逻辑

  2. 通用规范:抽取 BaseEntity 基类,统一管理通用字段

  3. 填充规则:严格填充优先,有值不覆盖,null 不填充

  4. 业务优化:动态获取登录用户 ID,时间统一用 LocalDateTime

  5. 避坑关键:Spring 托管处理器、字段类型匹配、更新用对 API、参数名规范