MyBatis Plus 提供了自动填充(MetaObjectHandler)的功能,可以在插入和更新操作时自动填充指定的字段,例如创建时间、更新时间、创建人、更新人等。这样可以简化开发,减少重复的代码编写,提高开发效率。官方教程:https://baomidou.com/guides/auto-fill-field/

在 MyBatis Plus 中,要实现自动填充功能,通常需要创建一个实现了 MetaObjectHandler 接口的类,并在该类中重写相应的方法来实现字段的填充逻辑。常用的方法包括 insertFill 和 updateFill,分别用于在插入和更新操作时填充字段的值。

以下是一个简单的示例:

@Component
public class MyMetaObjectHandler implements MetaObjectHandler {

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

    @Override
    public void updateFill(MetaObject metaObject) {
        if (getFieldValByName("updateTime", metaObject) == null) {
            this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
        }
        if (getFieldValByName("updateUser", metaObject) == null) {
            this.strictUpdateFill(metaObject, "updateUser", String.class, "system");
        }
    }
}

在这个示例中,我们创建了一个名为 MyMetaObjectHandler 的类,实现了 MetaObjectHandler 接口,并重写了 insertFill和 updateFill 方法。在 insertFill 方法中,我们为 createTime 和 createUser 字段如果为空的时候填充了默认值,在 updateFill 方法中,为 updateTime 和 updateUser 字段填充了默认值。

通过这样的配置,当执行插入或更新操作时,MyBatis Plus 将自动填充这些字段的值,而无需手动编写重复的代码。

这种自动填充功能可以大大简化开发工作,尤其是在需要对多个表进行相同字段填充的情况下,能够提高开发效率并减少出错的可能性。

来自工作改装的代码

1、定义一个:BaseDO

注意有一点我暂时还没弄清楚 涉及自动填充的类,是否必须有一个属性被注解修饰:@TableField(fill = ) ,但是实际上:MetaObjectHandler实现类,全部都添加了某些属性的!不知道这个fill标记是否有鸟用,记录下但没测试!

import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableLogic;
import lombok.Data;
import org.apache.ibatis.type.JdbcType;

import java.io.Serializable;
import java.time.LocalDateTime;

@Data
public abstract class BaseDO implements Serializable {
    /**
     * 创建时间
     */
    @TableField(fill = FieldFill.INSERT)
    private LocalDateTime createTime;

    /**
     * 最后更新时间
     */
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private LocalDateTime updateTime;

    /**
     * 创建者,使用 String 类型的原因是,未来可能会存在非数值的情况,留好拓展性。
     */
    @TableField(fill = FieldFill.INSERT, jdbcType = JdbcType.VARCHAR)
    private String creatorId;

    /**
     * 更新人,使用 String 类型的原因是,未来可能会存在非数值的情况,留好拓展性。
     */
    @TableField(fill = FieldFill.INSERT_UPDATE, jdbcType = JdbcType.VARCHAR)
    private String updaterId;

    /**
     * 是否删除 0=不删除 1=删除
     */
    @TableLogic
    private Byte deleted;
}

2、编写自动填充逻辑:MyBatisPlusAutoFIllConfig


import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import com.zanglikun.springdataredisdemo.pojo.BaseDO;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.time.LocalDateTime;
import java.util.Objects;

@Configuration
public class MyBatisPlusAutoFIllConfig implements MetaObjectHandler {
    /**
     * 插入元对象字段填充(用于插入时对公共字段的填充)
     *
     * @param metaObject 元对象
     */
    @Override
    public void insertFill(MetaObject metaObject) {
        // 插入对象必须Object
        if (Objects.nonNull(metaObject) && metaObject.getOriginalObject() instanceof BaseDO) {
            BaseDO baseDO = (BaseDO) metaObject.getOriginalObject();

            LocalDateTime current = LocalDateTime.now();
            // 创建时间为空,则以当前时间为插入时间
            if (Objects.isNull(baseDO.getCreateTime())) {
                baseDO.setCreateTime(current);
            }
            // 更新时间为空,则以当前时间为更新时间
            if (Objects.isNull(baseDO.getUpdateTime())) {
                baseDO.setUpdateTime(current);
            }

            String userId = getLoginUserId();
            // 默认一个用户
            if (StringUtils.isEmpty(userId)) {
                userId = "0";
            }
            // 当前登录用户不为空,创建人为空,则当前登录用户为创建人
            if (StringUtils.isEmpty(baseDO.getCreatorId())) {
                baseDO.setCreatorId(userId);
            }
            // 当前登录用户不为空,更新人为空,则当前登录用户为更新人
            if (StringUtils.isEmpty(baseDO.getUpdaterId())) {
                baseDO.setUpdaterId(userId);
            }
        }
    }


    /**
     * 更新元对象字段填充(用于更新时对公共字段的填充)
     *
     * @param metaObject 元对象
     */
    @Override
    public void updateFill(MetaObject metaObject) {
        // 更新时间为空,则以当前时间为更新时间
        Object modifyTime = getFieldValByName("updateTime", metaObject);
        if (Objects.isNull(modifyTime)) {
            setFieldValByName("updateTime", LocalDateTime.now(), metaObject);
        }

        // 当前登录用户不为空,更新人为空,则当前登录用户为更新人
        Object modifier = getFieldValByName("updaterId", metaObject);
        String userId = getLoginUserId();
        //默认一个用户
        if (StrUtil.isEmpty(userId)) {
            userId = "0";
        }
        if (Objects.isNull(modifier)) {
            setFieldValByName("updaterId", userId, metaObject);
        }
    }

    /**
     * 自定义方法,从请求头重获取登录人信息。
     *
     * @return 登录信息
     */
    private String getLoginUserId() {
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        if (!(requestAttributes instanceof ServletRequestAttributes)) {
            return null;
        }
        ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) requestAttributes;
        return servletRequestAttributes.getRequest().getHeader("userId");
    }
}

高级案例


import cn.hutool.core.date.DateTime;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.stereotype.Component;

import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.Arrays;
import java.util.Date;
import java.util.List;

/**
 * MyBatis Plus 元数据自动填充处理器
 * 支持 LocalDateTime 和 Date 和Hutool的DateTime类型
 *
 * @author : zanglikun
 * @date : 2025/10/22 14:48
 */
@Component
@ConditionalOnMissingBean(MetaObjectHandler.class)
public class AdvancedMetaObjectHandler implements MetaObjectHandler {

    // todo 1、请结合实际的项目 移除不需要的字段名称
    /* 时间字段别名集合 */
    private static final List<String> CREATE_TIME_FIELDS = Arrays.asList("createTime", "createdAt", "gmtCreate");
    private static final List<String> UPDATE_TIME_FIELDS = Arrays.asList("updateTime", "modifiedAt", "gmtModified");
    /* 用户字段 */
    private static final List<String> CREATE_USER_FIELDS = Arrays.asList("createUser", "creator", "creatorId");
    private static final List<String> UPDATE_USER_FIELDS = Arrays.asList("updateUser", "updater", "updaterId");
    /* 其他通用上下文字段 */
    private static final List<String> TENANT_FIELDS = Arrays.asList("tenantId");
    private static final List<String> ORG_FIELDS = Arrays.asList("orgId", "organizationId");
    private static final List<String> REQUEST_FIELDS = Arrays.asList("requestId", "traceId");
    private static final List<String> IP_FIELDS = Arrays.asList("clientIp", "ip", "remoteIp");
    private static final List<String> DELETE_FLAG_FIELDS = Arrays.asList("deleteFlag", "isDeleted", "delFlag");

    /* Long 类型是否写入毫秒(true)还是秒 */
    private static final boolean LONG_STORE_EPOCH_MILLI = true;

    /* String 类型默认时间格式 */
    private static final DateTimeFormatter STRING_TIME_FORMATTER =
            DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").withZone(ZoneId.systemDefault());

    @Override
    public void insertFill(MetaObject metaObject) {
        // 创建时间(仅空时填充)
        CREATE_TIME_FIELDS.forEach(f -> fillTimeIfAbsent(metaObject, f));

        // 创建人(仅空时填充)
        // CREATE_USER_FIELDS.forEach(f -> fillIfAbsent(metaObject, f, resolveUser()));

        // 租户、组织、请求、IP
        // TENANT_FIELDS.forEach(f -> fillIfAbsent(metaObject, f, ""));

        // ORG_FIELDS.forEach(f -> fillIfAbsent(metaObject, f, ""));
        // REQUEST_FIELDS.forEach(f -> fillIfAbsent(metaObject, f, ""));
        // IP_FIELDS.forEach(f -> fillIfAbsent(metaObject, f, ""));

        // 逻辑删除标识(仅空时;统一用 "0" 表示未删除,"1" 已删除)
        DELETE_FLAG_FIELDS.forEach(f -> fillIfAbsent(metaObject, f, "0"));
    }

    @Override
    public void updateFill(MetaObject metaObject) {
        // 更新时间(强制覆盖)
        UPDATE_TIME_FIELDS.forEach(f -> forceFillTime(metaObject, f));

        // 更新人(可选择覆盖策略:此处若为空才填;若需每次覆盖改为 forceFillValue)
        // UPDATE_USER_FIELDS.forEach(f -> fillIfAbsent(metaObject, f, resolveUser()));

        // 链路信息(请求/IP 通常每次变化,可强制)
        // REQUEST_FIELDS.forEach(f -> forceFillValue(metaObject, f, ""));

        // IP_FIELDS.forEach(f -> forceFillValue(metaObject, f, ""));
    }

    /* ==== 通用填充方法 ==== */

    private void fillIfAbsent(MetaObject metaObject, String field, Object val) {
        if (!hasSetter(metaObject, field)) return;
        if (getFieldValByName(field, metaObject) == null && val != null) {
            setFieldValByName(field, castValue(metaObject, field, val), metaObject);
        }
    }

    private void forceFillValue(MetaObject metaObject, String field, Object val) {
        if (!hasSetter(metaObject, field) || val == null) return;
        setFieldValByName(field, castValue(metaObject, field, val), metaObject);
    }

    private void fillTimeIfAbsent(MetaObject metaObject, String field) {
        if (!hasSetter(metaObject, field)) return;
        if (getFieldValByName(field, metaObject) == null) {
            setFieldValByName(field, resolveNow(metaObject, field), metaObject);
        }
    }

    private void forceFillTime(MetaObject metaObject, String field) {
        if (!hasSetter(metaObject, field)) return;
        setFieldValByName(field, resolveNow(metaObject, field), metaObject);
    }

    private boolean hasSetter(MetaObject metaObject, String field) {
        try {
            return metaObject.hasSetter(field);
        } catch (Exception e) {
            return false;
        }
    }

    /* ==== 时间解析与类型匹配 ==== */
    private Object resolveNow(MetaObject metaObject, String field) {
        Class<?> type = safeSetterType(metaObject, field);
        Instant now = Instant.now();

        if (type == null) return DateTime.now();
        if (type == LocalDateTime.class) return LocalDateTime.now();
        if (type == Date.class) return new Date();
        if (type == DateTime.class) return DateTime.now();
        if (type == Instant.class) return now;
        if (type == Long.class || type == long.class) {
            return LONG_STORE_EPOCH_MILLI ? now.toEpochMilli() : now.getEpochSecond();
        }
        if (type == String.class) {
            return STRING_TIME_FORMATTER.format(now);
        }
        // 未知类型:返回 Hutool DateTime 兜底
        return DateTime.now();
    }

    /**
     * 将外部值强转为字段 setter 类型(用于用户/租户等通用字段)
     */
    private Object castValue(MetaObject metaObject, String field, Object val) {
        Class<?> type = safeSetterType(metaObject, field);
        if (type == null || val == null) return val;
        if (type.isInstance(val)) return val;
        String s = String.valueOf(val);
        if (type == String.class) return s;
        if ((type == Long.class || type == long.class)) {
            try {
                return Long.parseLong(s);
            } catch (Exception ignored) {
                return null;
            }
        }
        if (type == Integer.class || type == int.class) {
            try {
                return Integer.parseInt(s);
            } catch (Exception ignored) {
                return null;
            }
        }
        // 简化:其他类型直接返回原值,或 null
        return val;
    }

    private Class<?> safeSetterType(MetaObject metaObject, String field) {
        try {
            return metaObject.getSetterType(field);
        } catch (Exception e) {
            return null;
        }
    }

    /* ==== 用户上下文 ==== */

    private String resolveUser() {
        String uid = "";
        return (uid == null || uid.isEmpty()) ? "system" : uid;
    }
}
特殊说明:
上述文章均是作者实际操作后产出。烦请各位,请勿直接盗用!转载记得标注原文链接:www.zanglikun.com
第三方平台不会及时更新本文最新内容。如果发现本文资料不全,可访问本人的Java博客搜索:标题关键字。以获取最新全部资料 ❤

免责声明:
本站文章旨在总结学习互联网技术过程中的经验与见解。任何人不得将其用于违法或违规活动!所有违规内容均由个人自行承担,与作者无关。