MyBatis-Plus 日期时间自动填充实践指南

一、基础配置与实现

1.1 实体类字段注解配置

import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import java.util.Date;

@Data
public class User {
    @TableId(type = IdType.AUTO)
    private Long id;
    
    private String username;
    
    // 插入时自动填充当前时间
    @TableField(fill = FieldFill.INSERT)
    private Date createTime;
    
    // 插入和更新时自动填充当前时间
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Date updateTime;
    
    // 逻辑删除字段(删除时自动填充当前时间)
    @TableLogic
    @TableField(fill = FieldFill.UPDATE)
    private Date deleteTime;
}
图片[1]_MyBatis-Plus 日期时间自动填充实践指南_知途无界

1.2 元对象处理器实现

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

import java.util.Date;

@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
    
    /**
     * 插入时自动填充
     */
    @Override
    public void insertFill(MetaObject metaObject) {
        this.strictInsertFill(metaObject, "createTime", Date.class, new Date());
        this.strictInsertFill(metaObject, "updateTime", Date.class, new Date());
    }
    
    /**
     * 更新时自动填充
     */
    @Override
    public void updateFill(MetaObject metaObject) {
        this.strictUpdateFill(metaObject, "updateTime", Date.class, new Date());
    }
}

二、高级配置与优化

2.1 多种日期格式支持

@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
    
    // 使用Java8的日期时间API
    @Override
    public void insertFill(MetaObject metaObject) {
        this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now());
        this.strictInsertFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
    }
    
    // 支持时间戳格式
    public void insertFill(MetaObject metaObject) {
        this.strictInsertFill(metaObject, "createTime", Long.class, System.currentTimeMillis());
    }
}

2.2 条件填充控制

@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
    
    @Override
    public void insertFill(MetaObject metaObject) {
        // 只有特定表才填充
        if (metaObject.getOriginalObject() instanceof User) {
            this.strictInsertFill(metaObject, "createTime", Date.class, new Date());
        }
        
        // 根据字段值决定是否填充
        Object status = metaObject.getValue("status");
        if (status != null && (Integer)status == 1) {
            this.strictInsertFill(metaObject, "activeTime", Date.class, new Date());
        }
    }
}

三、多场景实践方案

3.1 审计字段完整配置

@Data
public class BaseEntity {
    @TableField(fill = FieldFill.INSERT)
    private Long createBy;
    
    @TableField(fill = FieldFill.INSERT)
    private Date createTime;
    
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Long updateBy;
    
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Date updateTime;
}

// 在处理器中获取当前用户信息
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
    
    @Autowired
    private CurrentUserService userService;
    
    @Override
    public void insertFill(MetaObject metaObject) {
        Long userId = userService.getCurrentUserId();
        this.strictInsertFill(metaObject, "createBy", Long.class, userId);
        this.strictInsertFill(metaObject, "createTime", Date.class, new Date());
    }
    
    @Override
    public void updateFill(MetaObject metaObject) {
        Long userId = userService.getCurrentUserId();
        this.strictUpdateFill(metaObject, "updateBy", Long.class, userId);
        this.strictUpdateFill(metaObject, "updateTime", Date.class, new Date());
    }
}

3.2 多租户场景下的时间填充

@Component
public class TenantMetaObjectHandler implements MetaObjectHandler {
    
    @Override
    public void insertFill(MetaObject metaObject) {
        // 填充租户ID和时间
        this.strictInsertFill(metaObject, "tenantId", String.class, TenantContext.getCurrentTenant());
        this.strictInsertFill(metaObject, "createTime", Date.class, new Date());
    }
    
    @Override
    public void updateFill(MetaObject metaObject) {
        // 只更新时间字段
        this.strictUpdateFill(metaObject, "updateTime", Date.class, new Date());
    }
}

四、常见问题解决方案

4.1 时区问题处理

@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
    
    private Date getCurrentTime() {
        // 设置时区为东八区
        TimeZone.setDefault(TimeZone.getTimeZone("Asia/Shanghai"));
        return new Date();
    }
    
    @Override
    public void insertFill(MetaObject metaObject) {
        this.strictInsertFill(metaObject, "createTime", Date.class, getCurrentTime());
    }
}

4.2 字段未自动填充排查

  1. 检查项​:
    • 实体类字段是否有@TableField(fill = ...)注解
    • 处理器类是否有@Component注解
    • 字段名称是否与处理器中一致
    • 字段类型是否匹配
  2. 调试方法​: @Override public void insertFill(MetaObject metaObject) { System.out.println("Insert fill triggered for: " + metaObject.getOriginalObject().getClass().getName()); this.strictInsertFill(metaObject, "createTime", Date.class, new Date()); }

五、性能优化建议

5.1 批量插入优化

@Component
public class BatchInsertMetaObjectHandler implements MetaObjectHandler {
    
    private ThreadLocal<Date> batchTime = new ThreadLocal<>();
    
    public void startBatch() {
        batchTime.set(new Date());
    }
    
    public void endBatch() {
        batchTime.remove();
    }
    
    @Override
    public void insertFill(MetaObject metaObject) {
        Date now = batchTime.get() != null ? batchTime.get() : new Date();
        this.strictInsertFill(metaObject, "createTime", Date.class, now);
    }
}

// 使用示例
public void batchInsert(List<User> users) {
    try {
        metaObjectHandler.startBatch();
        userService.saveBatch(users);
    } finally {
        metaObjectHandler.endBatch();
    }
}

5.2 缓存时间对象

@Component
public class CachedTimeMetaObjectHandler implements MetaObjectHandler {
    
    private volatile long lastTimestamp = 0;
    private volatile Date cachedDate = null;
    
    private Date getCachedDate() {
        long now = System.currentTimeMillis();
        if (now - lastTimestamp > 1000) { // 1秒缓存
            lastTimestamp = now;
            cachedDate = new Date(now);
        }
        return cachedDate;
    }
    
    @Override
    public void insertFill(MetaObject metaObject) {
        this.strictInsertFill(metaObject, "createTime", Date.class, getCachedDate());
    }
}

六、Spring Boot 完整配置示例

6.1 完整配置类

@Configuration
@EnableTransactionManagement
@MapperScan("com.example.mapper")
public class MybatisPlusConfig {
    
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        // 添加分页插件
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }
    
    @Bean
    public MetaObjectHandler metaObjectHandler() {
        return new MyMetaObjectHandler();
    }
}

6.2 完整处理器实现

@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
    
    private static final Logger logger = LoggerFactory.getLogger(MyMetaObjectHandler.class);
    
    @Override
    public void insertFill(MetaObject metaObject) {
        logger.debug("Start insert fill...");
        try {
            this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now());
            this.strictInsertFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
            
            // 获取当前用户
            Long userId = getCurrentUserId();
            if (userId != null) {
                this.strictInsertFill(metaObject, "createBy", Long.class, userId);
            }
        } catch (Exception e) {
            logger.error("Auto fill failed during insert", e);
        }
    }
    
    @Override
    public void updateFill(MetaObject metaObject) {
        logger.debug("Start update fill...");
        try {
            this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
            
            // 获取当前用户
            Long userId = getCurrentUserId();
            if (userId != null) {
                this.strictUpdateFill(metaObject, "updateBy", Long.class, userId);
            }
        } catch (Exception e) {
            logger.error("Auto fill failed during update", e);
        }
    }
    
    private Long getCurrentUserId() {
        // 实现获取当前用户ID的逻辑
        try {
            Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
            if (authentication != null && authentication.getPrincipal() instanceof UserDetails) {
                return ((CustomUserDetails) authentication.getPrincipal()).getUserId();
            }
        } catch (Exception e) {
            logger.warn("Get current user id failed", e);
        }
        return null;
    }
}

七、版本兼容性说明

MyBatis-Plus 版本特性变化
3.0.x基础自动填充功能
3.1.0+添加strictInsertFill/strictUpdateFill方法
3.4.0+支持LocalDateTime等Java8时间API
3.5.0+优化填充逻辑,支持更多场景

最佳实践建议​:

  1. 对于新项目,建议使用最新的MyBatis-Plus版本(≥3.5.0)
  2. 生产环境建议使用Java8时间API(LocalDateTime)
  3. 审计字段(创建人、更新时间等)建议统一放在基类中
  4. 对于批量操作,考虑使用缓存时间提升性能
© 版权声明
THE END
喜欢就点个赞,支持一下吧!
点赞56 分享
评论 抢沙发
头像
欢迎您留下评论!
提交
头像

昵称

取消
昵称表情代码图片

    暂无评论内容