一、应用场景
以下场景适合使用公共字段自动填充:
- 创建时间:记录数据插入时间。
- 更新时间:记录数据最后一次修改时间。
- 操作人:记录当前操作用户ID或姓名。
二、方案一:MyBatis-Plus的MetaObjectHandler
MyBatis-Plus(简称MP)提供了 MetaObjectHandler 接口,通过实现该接口,可以在插入或更新时自动填充字段。
2.1 实现步骤
步骤1:实体类添加注解
在需要自动填充的字段上添加 @TableField 注解,并指定填充策略:
public class User {
@TableField(fill = FieldFill.INSERT)
private Date createTime;
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date updateTime;
@TableField(fill = FieldFill.INSERT)
private String createBy;
}
步骤2:实现MetaObjectHandler接口
创建一个类实现 MetaObjectHandler,定义填充逻辑:
@Component
public class AutoFillHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
// 获取当前用户(需结合业务实现)
String currentUser = getCurrentUser();
this.strictInsertFill(metaObject, "createTime", Date.class, new Date());
this.strictInsertFill(metaObject, "createBy", String.class, currentUser);
this.strictInsertFill(metaObject, "updateTime", Date.class, new Date());
}
@Override
public void updateFill(MetaObject metaObject) {
this.strictUpdateFill(metaObject, "updateTime", Date.class, new Date());
}
private String getCurrentUser() {
// 从ThreadLocal或SecurityContext中获取用户信息
return "admin";
}
}
步骤3:配置MP自动填充
在 application.yml 中启用MP配置:
mybatis-plus:
global-config:
db-config:
logic-not-delete-value: 0
logic-delete-value: 1
configuration:
map-underscore-to-camel-case: true
三、方案二:MyBatis拦截器
如果项目未使用MyBatis-Plus,可以通过MyBatis的拦截器(Interceptor)实现类似功能。
3.1 实现步骤
步骤1:自定义拦截器
实现 Interceptor 接口,拦截插入和更新操作:
@Intercepts({
@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})
})
public class AutoFillInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];
Object parameter = invocation.getArgs()[1];
// 判断操作类型:INSERT或UPDATE
SqlCommandType sqlCommandType = mappedStatement.getSqlCommandType();
if (parameter != null) {
MetaObject metaObject = SystemMetaObject.forObject(parameter);
if (sqlCommandType == SqlCommandType.INSERT) {
// 填充创建时间和创建人
setFieldIfNull(metaObject, "createTime", new Date());
setFieldIfNull(metaObject, "createBy", getCurrentUser());
}
if (sqlCommandType == SqlCommandType.INSERT || sqlCommandType == SqlCommandType.UPDATE) {
// 填充更新时间
setFieldIfNull(metaObject, "updateTime", new Date());
}
}
return invocation.proceed();
}
private void setFieldIfNull(MetaObject metaObject, String fieldName, Object value) {
if (metaObject.getValue(fieldName) == null) {
metaObject.setValue(fieldName, value);
}
}
private String getCurrentUser() {
// 获取当前用户(如从ThreadLocal中读取)
return "admin";
}
}
步骤2:注册拦截器
将拦截器添加到MyBatis配置中:
@Configuration
public class MyBatisConfig {
@Bean
public AutoFillInterceptor autoFillInterceptor() {
return new AutoFillInterceptor();
}
}
四、方案对比与注意事项
4.1 方案对比
| 特性 | MyBatis-Plus MetaObjectHandler | MyBatis拦截器 |
|---|---|---|
| 实现复杂度 | 简单(仅需实现接口) | 中等(需处理拦截逻辑) |
| 灵活性 | 依赖MP框架,填充策略固定 | 完全自主控制,可处理复杂逻辑 |
| 侵入性 | 低(通过注解配置) | 低(无侵入) |
| 适用场景 | 已使用MyBatis-Plus的项目 | 原生MyBatis或需要高度定制的场景 |
4.2 注意事项
- 字段名称一致性:确保实体类字段名与数据库列名一致(可通过
@Column注解映射)。 - 空值覆盖问题:拦截器中需判断字段是否已赋值,避免覆盖业务代码手动设置的值。
- 分布式环境:
getCurrentUser()需结合分布式Session或JWT Token获取用户信息。 - 性能影响:拦截器会增加少量性能开销,避免在拦截器中处理耗时逻辑。
五、总结
通过 MyBatis-Plus的MetaObjectHandler 或 MyBatis拦截器,均可实现公共字段的自动填充:
- MyBatis-Plus方案 适合快速开发,通过注解和接口简化配置。
- 拦截器方案 更灵活,适用于原生MyBatis或需要复杂填充逻辑的场景。
选择方案时,需结合项目技术栈和需求,优先使用MyBatis-Plus以提升开发效率。