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;
}
}
第三方平台不会及时更新本文最新内容。如果发现本文资料不全,可访问本人的Java博客搜索:标题关键字。以获取最新全部资料 ❤
免责声明: 本站文章旨在总结学习互联网技术过程中的经验与见解。任何人不得将其用于违法或违规活动!所有违规内容均由个人自行承担,与作者无关。
