MyBatis-Plus 提供了强大的字段自动填充功能,通过 @TableField(fill = FieldFill.XXX)
注解与元对象处理器(MetaObjectHandler
)配合,可自动为指定字段(如 create_time
、update_time
、create_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 提供的接口,用于实现自动填充逻辑。
- 需要实现
insertFill
和updateFill
方法。 - 框架在执行
insert
或update
操作前,会自动调用对应方法填充字段。
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 |
四、注意事项
@Component
必不可少:确保处理器被 Spring 管理。- 字段可为
null
:数据库字段应允许NULL
,由程序填充。 - 避免循环填充:不要在
insertFill
中调用update
操作。 - 类型匹配:
strictInsertFill
第三个参数必须与字段类型一致(如LocalDateTime.class
)。 - 逻辑删除字段:不要对
@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 + 全局处理器 |
🔧 推荐流程:
- 定义实体字段并加
@TableField(fill = ...)
- 创建
MetaObjectHandler
实现填充逻辑 - 使用
strictInsertFill
/strictUpdateFill
方法 - 测试插入/更新操作是否自动填充