在现代 Web 应用开发中,我们经常需要在数据库表中记录数据的创建时间、创建者、更新时间和更新者信息,这些字段(通常是 createTime、createBy、updateTime 和 updateBy)在很多实体类中都需要存在。如果每次都手动设置这些字段,不仅会产生大量的重复代码,还会降低开发效率,更可能导致数据不一致。MyBatis 提供了一个强大的接口 MetaObjectHandler,可以帮助我们优雅地统一处理这些字段,让代码更加简洁、易维护。
本文将深入探讨如何使用 MyBatis 的 MetaObjectHandler 来自动填充实体类的 createTime、createBy、updateTime 和 updateBy 字段,并提供完整的代码示例和最佳实践。
一、为什么需要 MetaObjectHandler?
- 避免重复代码: 无需在每个 Service 或 Controller 中重复编写字段填充代码,减少代码冗余。
- 提高开发效率:只需专注于业务逻辑,无需关注这些通用字段,提高开发效率。
- 确保数据一致性: 通过统一的处理逻辑,确保 create 和 update数据的一致性。
- 方便数据审计: 统一处理这些字段,方便数据审计和追溯。
- 代码简洁:将字段处理逻辑集中到一个地方,提高代码的可读性和可维护性。
二、MyBatis MetaObjectHandler 原理
MetaObjectHandler 是 MyBatis 提供的一个接口,它允许我们在 MyBatis 执行 SQL 语句之前或之后拦截并修改元对象(MetaObject)的属性。 MetaObject 封装了实体对象,我们可以通过 MetaObject 来访问和修改实体类的字段值。MetaObjectHandler 提供了两个关键方法:
insertFill(MetaObject metaObject)
: 在执行 INSERT 语句之前调用,用于填充新增数据时的字段。updateFill(MetaObject metaObject)
: 在执行 UPDATE 语句之前调用,用于填充更新数据时的字段。
三、实践:使用 MetaObjectHandler 自动填充字段
3.1 创建公共实体类(BaseEntity)
@Data
public class BaseEntity<T> extends Model {
/**
* 创建人
*/
@ApiModelProperty(value = "创建人")
@TableField(value = "create_by", fill = FieldFill.INSERT)
private String createBy;
/**
* 创建时间
*/
@ApiModelProperty(value = "创建时间")
@TableField(value = "create_time", fill = FieldFill.INSERT)
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private LocalDateTime createTime;
/**
* 更新人
*/
@ApiModelProperty(value = "更新人")
@TableField(value = "update_by", fill = FieldFill.INSERT_UPDATE)
private String updateBy;
/**
* 更新时间
*/
@ApiModelProperty(value = "更新时间")
@TableField(value = "update_time", fill = FieldFill.INSERT_UPDATE)
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private LocalDateTime updateTime;
/**
* 备注
*/
@ApiModelProperty(value = "备注")
@TableField(value = "remark")
private String remark;
}
3.2 创建 MetaObjectHandler 实现类
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
@Component
public class MybatisMetaObjectHandler implements MetaObjectHandler {
/**
* 新增时填充创建人和创建时间
* @param metaObject
*/
@Override
public void insertFill(MetaObject metaObject) {
this.strictInsertFill(metaObject, "createBy", String.class, getUsername());
this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now());
}
/**
* 更新时填充更新人和更新时间
* @param metaObject
*/
@Override
public void updateFill(MetaObject metaObject) {
this.strictInsertFill(metaObject, "updateBy", String.class, getUsername());
this.strictInsertFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
}
private String getUsername() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication == null || !authentication.isAuthenticated()) {
return "system"; // 默认值
}
return authentication.getName();
}
}
insertFill
方法:设置 createTime、createBy的值。updateFill
方法:设置 updateTime 和updateBy 的值。strictInsertFill
和strictUpdateFill
方法:是 MyBatis Plus 提供的便利方法,会自动处理类型转换和字段存在性检查。getUsername
方法: 从 Spring Security 的上下文中获取用户名,如果没取到则使用默认值,需要根据实际情况修改。
3.3 创建数据库表
CREATE TABLE `t_user` (
`id` int NOT NULL AUTO_INCREMENT COMMENT '用户ID',
`user_name` varchar(255) DEFAULT NULL COMMENT '用户名称',
`user_desc` varchar(255) DEFAULT NULL COMMENT '用户描述',
`create_by` varchar(64) NOT NULL COMMENT '创建人',
`create_time` datetime NOT NULL COMMENT '创建时间',
`update_by` varchar(64) DEFAULT NULL COMMENT '更新人',
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
`remark` varchar(500) DEFAULT NULL COMMENT '备注'
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3
3.4 使用生成的实体类继承BaseEntity
public class User extends BaseEntity<User> {
private static final long serialVersionUID = 1L;
@TableId(value = "id", type = IdType.AUTO)
private Integer id;
@TableField("user_name")
private String userName;
@TableField("user_desc")
private String userDesc;
@Override
public Serializable pkVal() {
return this.id;
}
}
现在,你可以调用 insert 和 update 操作,MetaObjectHandler 将会自动设置这些字段的值,无需任何额外处理。
四、注意事项
- 字段类型一致性: 确保实体类中字段的类型和
MetaObjectHandler
中设置的类型一致,避免类型转换错误。 - 获取用户信息:
getUsername
方法需要根据你的实际应用场景进行修改,可以从 Spring Security上下文、Token 或其他地方获取当前用户信息。 - 配合Mybatis Plus使用: 以上代码使用的是 mybatis plus 的
MetaObjectHandler
。 - 数据库表字段: 数据库表字段设置
createTime
和createBy
不能更新, 并设置默认值。 - 更新操作:update 操作的时候可能会生成不了update_by和update_time,注意更新语句的写法。