一、核心概念
全局字段填充指在数据操作(插入/更新)时自动填充实体类中的特定字段(如 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.UPDATE 或 FieldFill.INSERT_UPDATE |
修正注解填充策略 |
时间值早于当前时间 | 服务器时区设置错误 | 统一使用 UTC 或 LocalDateTime.now() |
批量插入不生效 | 需使用 MP 的 saveBatch() 方法 |
避免手动循环插入 |
四、关键注意事项
类型严格匹配
使用strictInsertFill/strictUpdateFill
时需确保字段类型与参数类型一致:// ✅ 正确 this.strictInsertFill(metaObject, "price", BigDecimal.class, new BigDecimal("99.99")); // ❌ 错误:类型不匹配 this.strictInsertFill(metaObject, "price", Double.class, 99.99);
避免覆盖已有值
默认行为不覆盖已设置的值,需显式调用:// 强制覆盖 metaObject.setValue("updateBy", "admin");
多数据源场景
每个数据源需独立配置MetaObjectHandler
:@Bean public MetaObjectHandler metaObjectHandler(@Qualifier("ds1") DataSource dataSource) { return new Ds1MetaObjectHandler(); }
五、性能优化技巧
减少反射调用
使用strictFill
方法族替代通用setFieldValByName
:// 性能更优 (缓存了字段的 setter 方法) this.strictInsertFill(metaObject, "createTime", LocalDateTime::now, LocalDateTime.class);
避免复杂逻辑
不在insertFill/updateFill
中执行数据库查询等耗时操作。批量操作优化
使用 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 全局字段填充的核心要点,避免常见陷阱,并应用于实际项目中实现高效、安全的自动填充功能。重点注意字段类型匹配、批量操作支持和多数据源适配三大核心问题。