MyBatis-Plus 提供了强大的字段自动填充功能,通过 @TableField(fill = FieldFill.XXX) 注解与元对象处理器(MetaObjectHandler)配合,可自动为指定字段(如 create_timeupdate_timecreate_by 等)在插入或更新时填充数据,无需手动赋值,极大提升开发效率与数据一致性。


一、核心概念

1.1 @TableField(fill = ...) 注解

  • 用于标识某个字段需要进行自动填充。
  • fill 属性指定填充时机:
    • FieldFill.INSERT:仅插入时填充
    • FieldFill.UPDATE:仅更新时填充
    • FieldFill.INSERT_UPDATE:插入和更新都填充
    • FieldFill.DEFAULT:不填充(默认值)
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;

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

@TableField(fill = FieldFill.INSERT)
private String createBy;

@TableField(fill = FieldFill.INSERT_UPDATE)
private String updateBy;

1.2 MetaObjectHandler(元对象处理器)

  • 是 MyBatis-Plus 提供的接口,用于实现自动填充逻辑。
  • 需要实现 insertFillupdateFill 方法。
  • 框架在执行 insertupdate 操作前,会自动调用对应方法填充字段。

1.3 填充场景

字段 填充时机 说明
create_time INSERT 记录创建时间
update_time INSERT, UPDATE 每次修改都更新
create_by INSERT 创建人(如当前登录用户)
update_by INSERT, UPDATE 最后修改人

二、操作步骤(非常详细)

步骤 1:添加依赖(Maven)

确保已引入 MyBatis-Plus 核心依赖:

<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.5.3.1</version>
</dependency>

✅ 版本建议使用 3.4.0+,功能更稳定。


步骤 2:创建实体类并添加 @TableField(fill = ...) 注解

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

@Data
@TableName("user")
public class User {

    @TableId(type = IdType.AUTO)
    private Long id;

    private String name;

    private String email;

    /**
     * 创建时间:仅插入时填充
     */
    @TableField(fill = FieldFill.INSERT)
    private LocalDateTime createTime;

    /**
     * 更新时间:插入和更新时都填充
     */
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private LocalDateTime updateTime;

    /**
     * 创建人:仅插入时填充
     */
    @TableField(fill = FieldFill.INSERT)
    private String createBy;

    /**
     * 更新人:插入和更新时都填充
     */
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private String updateBy;

    /**
     * 逻辑删除字段(可选)
     */
    @TableLogic
    @TableField(fill = FieldFill.DEFAULT)
    private Integer deleted;
}

⚠️ 注意:

  • 字段在数据库中应允许 NULL,由程序自动填充。
  • 使用 LocalDateTime 推荐(Java 8+),也可用 Date

步骤 3:创建元对象处理器(MetaObjectHandler)

创建类实现 MetaObjectHandler 接口:

import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;

/**
 * 自定义元对象处理器,实现字段自动填充
 */
@Component  // 必须交给 Spring 管理
public class MyMetaObjectHandler implements MetaObjectHandler {

    /**
     * 插入时的填充策略
     */
    @Override
    public void insertFill(MetaObject metaObject) {
        // 设置创建时间
        this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now());
        // 设置更新时间(插入时也填充)
        this.strictInsertFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
        // 设置创建人(模拟当前用户,实际项目中从 SecurityContext 获取)
        this.strictInsertFill(metaObject, "createBy", String.class, getCurrentUser());
        // 设置更新人
        this.strictInsertFill(metaObject, "updateBy", String.class, getCurrentUser());
    }

    /**
     * 更新时的填充策略
     */
    @Override
    public void updateFill(MetaObject metaObject) {
        // 只更新 updateTime 和 updateBy
        this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
        this.strictUpdateFill(metaObject, "updateBy", String.class, getCurrentUser());
    }

    /**
     * 获取当前登录用户(模拟方法)
     * 实际项目中可从 Spring Security、JWT、ThreadLocal 等获取
     */
    private String getCurrentUser() {
        // 示例:从 Spring Security 获取
        // Authentication auth = SecurityContextHolder.getContext().getAuthentication();
        // return auth != null ? auth.getName() : "system";

        // 模拟用户
        return "admin";
    }
}

✅ 关键点:

  • 类上必须加 @Component,让 Spring 扫描到。
  • 使用 strictInsertFill / strictUpdateFill 方法更安全(推荐)。
  • 旧版本使用 setFieldValByName,已过时。

步骤 4:配置 MyBatis-Plus(可选)

application.yml 中启用自动填充(通常默认已启用):

mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl  # 打印 SQL
  global-config:
    db-config:
      # 主键类型 0:"数据库ID自增", 1:"用户输入ID", 2:"全局唯一ID", 3:"全局唯一ID"
      id-type: auto
      # 逻辑删除字段
      logic-delete-field: deleted
      logic-delete-value: 1
      logic-not-delete-value: 0

⚠️ 自动填充无需额外配置,只要 MetaObjectHandler 被 Spring 扫描即可。


步骤 5:使用 Mapper 进行 CRUD 操作

@Service
public class UserService {

    @Autowired
    private UserMapper userMapper;

    /**
     * 插入用户(无需手动设置时间与用户)
     */
    public void insertUser() {
        User user = new User();
        user.setName("张三");
        user.setEmail("zhangsan@example.com");
        // ❌ 不需要手动设置
        // user.setCreateTime(LocalDateTime.now());
        // user.setUpdateBy("admin");

        userMapper.insert(user); // 自动填充 createTime, updateTime, createBy, updateBy
    }

    /**
     * 更新用户
     */
    public void updateUser() {
        User user = userMapper.selectById(1L);
        user.setName("李四");

        userMapper.updateById(user); // 自动更新 updateTime 和 updateBy
    }
}

三、常见错误与解决方案

错误 原因 解决方案
字段未填充 MetaObjectHandler 未被 Spring 扫描 添加 @Component 注解
报错 No qualifying bean 未引入 Spring Context 确保是 Spring Boot 项目
填充 null 方法返回 null 检查 getCurrentUser() 等方法逻辑
setFieldValByName 过时 使用旧 API 改用 strictInsertFill / strictUpdateFill
更新时不填充 fill = FieldFill.INSERT 错误 改为 INSERT_UPDATE

四、注意事项

  1. @Component 必不可少:确保处理器被 Spring 管理。
  2. 字段可为 null:数据库字段应允许 NULL,由程序填充。
  3. 避免循环填充:不要在 insertFill 中调用 update 操作。
  4. 类型匹配strictInsertFill 第三个参数必须与字段类型一致(如 LocalDateTime.class)。
  5. 逻辑删除字段:不要对 @TableLogic 字段使用 fill,MP 会自动处理。

五、使用技巧

5.1 动态获取当前用户

private String getCurrentUser() {
    Authentication auth = SecurityContextHolder.getContext().getAuthentication();
    return auth != null && auth.isAuthenticated() ? auth.getName() : "anonymous";
}

5.2 使用 FieldFill 枚举优化代码

// 可定义常量
private static final String CREATE_TIME = "createTime";
private static final String UPDATE_TIME = "updateTime";

5.3 条件填充(根据业务逻辑)

@Override
public void insertFill(MetaObject metaObject) {
    Object createTime = getFieldValByName("createTime", metaObject);
    if (createTime == null) {
        this.strictInsertFill(metaObject, "createTime", LocalDateTime.now());
    }
}

5.4 支持 Date 类型

this.strictInsertFill(metaObject, "createTime", Date.class, new Date());

六、最佳实践与性能优化

6.1 最佳实践

统一基类(BaseEntity)
创建公共父类,避免重复定义:

@Data
public class BaseEntity {
    @TableField(fill = FieldFill.INSERT)
    private LocalDateTime createTime;

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

    @TableField(fill = FieldFill.INSERT)
    private String createBy;

    @TableField(fill = FieldFill.INSERT_UPDATE)
    private String updateBy;
}

// 实体类继承
public class User extends BaseEntity {
    private Long id;
    private String name;
}

日志审计
结合 @CreatedDate@LastModifiedDate(JPA)或自定义注解,实现审计日志。

数据库默认值兜底
在数据库层面也设置默认值(如 CURRENT_TIMESTAMP),防止程序未启用时数据缺失。

ALTER TABLE user 
MODIFY create_time DATETIME DEFAULT CURRENT_TIMESTAMP,
MODIFY update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP;

单元测试验证填充逻辑

@Test
public void testInsertFill() {
    User user = new User();
    user.setName("test");
    userMapper.insert(user);

    assertNotNull(user.getCreateTime());
    assertNotNull(user.getUpdateBy());
    assertEquals("admin", user.getCreateBy());
}

6.2 性能优化

  • 无运行时性能损耗:自动填充在 MyBatis 拦截器中执行,仅一次反射调用,性能几乎无影响。
  • 避免频繁获取用户信息:若 getCurrentUser() 复杂,可缓存 ThreadLocal。
  • 批量操作支持insertBatch 也支持自动填充(3.4.0+)。

七、总结

MyBatis-Plus 字段自动填充是提升开发效率的利器:

功能 实现方式
自动填充时间 @TableField(fill = INSERT_UPDATE) + MetaObjectHandler
自动填充用户 同上,getCurrentUser() 从上下文获取
统一管理 基类 BaseEntity + 全局处理器

🔧 推荐流程

  1. 定义实体字段并加 @TableField(fill = ...)
  2. 创建 MetaObjectHandler 实现填充逻辑
  3. 使用 strictInsertFill / strictUpdateFill 方法
  4. 测试插入/更新操作是否自动填充