
1. 项目概述还在为每个实体类手动设置创建时间、更新时间而烦恼吗作为Java开发者我们经常需要在数据库表中记录数据的创建和修改时间。传统做法是在每次插入或更新操作时手动设置这些字段值不仅代码冗余度高还容易遗漏。MyBatis-Plus提供的自动填充功能完美解决了这个问题。我最近在重构一个老项目时发现代码中充斥着大量重复的setCreateTime(new Date())这样的代码。通过引入TableField注解配合MetaObjectHandler成功将这些手动操作转换为自动处理代码量减少了30%以上。下面就来详细分享这个提升开发效率的利器。2. 核心原理与配置2.1 自动填充机制解析MyBatis-Plus的自动填充功能基于元对象处理器(MetaObjectHandler)实现其核心原理是拦截器机制在执行SQL操作前MyBatis-Plus会检查实体类中标记了TableField注解的字段填充策略判断根据fill属性值(INSERT/UPDATE/INSERT_UPDATE)决定当前操作是否需要填充值注入通过反射机制将MetaObjectHandler中定义的值注入到实体对象对应字段这种设计有三大优势无侵入性不需要修改原有业务逻辑代码高灵活性可以针对不同字段设置不同的填充策略强类型安全strictFill方法会进行类型校验2.2 完整配置步骤2.2.1 实体类配置首先需要在实体类中标记需要自动填充的字段public class User { TableField(fill FieldFill.INSERT) private LocalDateTime createTime; TableField(fill FieldFill.INSERT_UPDATE) private LocalDateTime updateTime; TableField(fill FieldFill.INSERT) private Long createUserId; // 其他字段... }FieldFill枚举的四种策略DEFAULT不处理INSERT插入时填充UPDATE更新时填充INSERT_UPDATE插入和更新时都填充2.2.2 实现MetaObjectHandler创建处理器类实现填充逻辑Slf4j Component public class AutoFillHandler implements MetaObjectHandler { Override public void insertFill(MetaObject metaObject) { this.strictInsertFill(metaObject, createTime, LocalDateTime.class, LocalDateTime.now()); this.strictInsertFill(metaObject, createUserId, Long.class, getCurrentUserId()); } Override public void updateFill(MetaObject metaObject) { this.strictUpdateFill(metaObject, updateTime, LocalDateTime.class, LocalDateTime.now()); } private Long getCurrentUserId() { // 从安全上下文获取当前用户ID // 实际项目中使用Spring Security或自定义认证体系 return 123456L; // 示例值 } }关键点说明strictInsertFill/strictUpdateFill方法提供了类型安全的填充方式方法参数依次为元对象、字段名、字段类型、填充值建议使用Java8的日期时间API(LocalDateTime)而非老旧的Date类2.2.3 Spring配置确保你的配置类能扫描到处理器Configuration ComponentScan(com.yourpackage.handler) public class MybatisPlusConfig { // 其他配置... }3. 高级用法与实战技巧3.1 多场景填充策略实际项目中我们可能需要更复杂的填充逻辑。例如public class Order { TableField(fill FieldFill.INSERT) private String orderNo; // 订单编号需要特殊生成规则 TableField(fill FieldFill.INSERT) private Integer orderStatus; } Component public class OrderAutoFillHandler implements MetaObjectHandler { Override public void insertFill(MetaObject metaObject) { // 订单特殊处理 if (metaObject.getOriginalObject() instanceof Order) { this.strictInsertFill(metaObject, orderNo, String.class, generateOrderNo()); this.strictInsertFill(metaObject, orderStatus, Integer.class, 0); } } private String generateOrderNo() { return ORD System.currentTimeMillis(); } }3.2 填充优先级控制当多个处理器存在时可以通过Order注解控制执行顺序Order(1) Component public class BaseAutoFillHandler implements MetaObjectHandler { // 基础字段填充 } Order(2) Component public class BusinessAutoFillHandler implements MetaObjectHandler { // 业务字段填充 }3.3 动态填充值有时我们需要根据运行时条件决定填充值Override public void insertFill(MetaObject metaObject) { if (isInternalRequest()) { this.strictInsertFill(metaObject, source, Integer.class, 1); } else { this.strictInsertFill(metaObject, source, Integer.class, 2); } }4. 常见问题排查4.1 填充不生效的六大原因注解配置错误确保TableField的fill属性值正确检查字段名是否与处理器中一致区分大小写处理器未生效确认类上有Component或Bean注解检查包路径是否被Spring扫描类型不匹配处理器中的字段类型必须与实体类完全一致注意基本类型和包装类型的区别更新操作问题使用update(entity, wrapper)时entity不能为null纯条件更新update(wrapper)不支持自动填充填充策略冲突INSERT_UPDATE字段在更新时如果已有值可能不会被覆盖版本兼容性不同MyBatis-Plus版本API可能有差异4.2 性能优化建议避免在填充处理器中执行耗时操作对高频实体考虑使用缓存策略批量操作时注意填充效率5. 最佳实践总结经过多个项目的实践验证我总结了以下经验字段设计规范所有表都应包含create_time和update_time字段使用统一的字段命名规范如createBy/updateBy时间字段建议使用timestamp或datetime类型处理器实现建议按业务域拆分多个处理器添加详细的日志记录对关键字段进行非空校验测试策略编写单元测试验证填充逻辑集成测试中检查数据库实际值模拟并发场景测试线程安全性扩展思考可以结合审计日志功能与工作流引擎集成支持多租户场景这套自动填充机制不仅适用于简单的时间字段还可以扩展到各种需要自动维护的字段场景。比如操作人跟踪、数据版本控制、软删除标记等。合理使用可以显著减少样板代码提高开发效率的同时降低出错概率。