Mybatis 基础字段自动维护
670 字
3 分钟
Mybatis 基础字段自动维护
当前系统中,表的 创建日期、更新日期,整理了有以下取名
| 创建日期 | 更新日期 |
|---|---|
| create_ts | last_mnt_ts |
| created_time | updated_time |
| create_time | update_ts |
| update_time | |
| modified_ts |
在使用 Mybatis Plus 时,其自带的 自动填充功能,遇到这种基础字段命名不统一的场景时,不够灵活,所以有了以下的自定义插件,通过 在不同的基础数据库实体映射类中,标记对应的 创建日期、更新日期等字段,满足基础字段自动填充功能。
自定义注解说明
| 注解 | 用途 |
|---|---|
| @Created | 填充记录创建时间 |
| @Modified | 填充记录最后更新时间 |
| @CreatedBy | 填充记录创建人 |
| @ModifiedBy | 填充记录更新人 |
使用例子
@Datapublic abstract class AbstractEntity implements Serializable {
@Serial private static final long serialVersionUID = -6362917626913479219L; /** * 创建人 */ @CreatedBy protected Long createdBy;
/** * 创建时间 */ @Created protected LocalDateTime createdTime;
/** * 创建日期 */ @ModifiedBy protected Long updatedBy;
/** * 更新时间 */ @Modified protected LocalDateTime updatedTime;
}底层实现
通过自定义 mybatis 的插件,拦截 Executor 的 update 方法,找到对应的实体类对象,进行值插入
@Intercepts({@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})})public class AutoFillInterceptor implements Interceptor {
private ConcurrentHashMap<Class, AuditingConfig> auditingConfigMap = new ConcurrentHashMap<>();
private CurrentUserExtractor currentUserExtractor;
public void setCurrentUserExtractor(CurrentUserExtractor currentUserExtractor) { this.currentUserExtractor = currentUserExtractor; }
@Override public Object intercept(Invocation invocation) throws Throwable { Object[] args = invocation.getArgs(); if (args.length > 1) { Object arg = args[1]; Class<?> clazz = arg.getClass(); AuditingConfig config = auditingConfigMap.computeIfAbsent(clazz, this::extractConfig); Field createField = config.getCreateField(); Field modifiedField = config.getModifiedField(); BeanWrapper beanWrapper = null; MappedStatement mappedStatement = (MappedStatement)args[0]; SqlCommandType sqlCommandType = mappedStatement.getSqlCommandType(); if (createField != null || modifiedField != null) { beanWrapper = new BeanWrapperImpl(arg); Date now = new Date(); if (sqlCommandType == SqlCommandType.INSERT) { if (createField != null) { setAuditingDateValue(beanWrapper, createField, now); } if (modifiedField != null) { setAuditingDateValue(beanWrapper, modifiedField, now); } // if (config.getVersionFiled() != null) { // setDefaultVersion(beanWrapper, config.getVersionFiled()); // } } else if (sqlCommandType == SqlCommandType.UPDATE) { setAuditingDateValue(beanWrapper, modifiedField, now); } } if (currentUserExtractor != null) { Field createdByField = config.getCreatedByField(); Field modifiedByField = config.getModifiedByField(); if (createdByField != null && modifiedByField != null) { Object user = currentUserExtractor.getCurrentUser(); if (beanWrapper == null) { beanWrapper = new BeanWrapperImpl(arg); } if (sqlCommandType == SqlCommandType.INSERT) { if (createField != null) { setAuditingUserValue(beanWrapper, createdByField, user); } if (modifiedField != null) { setAuditingUserValue(beanWrapper, modifiedByField, user); } } else if (sqlCommandType == SqlCommandType.UPDATE) { setAuditingUserValue(beanWrapper, modifiedByField, user); } } } } return invocation.proceed(); }
private void setAuditingDateValue(BeanWrapper beanWrapper, Field field, Date value) { String name = field.getName(); if (field.getType() == Date.class) { beanWrapper.setPropertyValue(name, value); } }
private void setAuditingUserValue(BeanWrapper beanWrapper, Field field, Object user) { String name = field.getName(); if (beanWrapper.getPropertyValue(name) == null) { beanWrapper.setPropertyValue(name, user); } }
private AuditingConfig extractConfig(Class<?> clazz) { AuditingConfig config = new AuditingConfig(); ReflectionUtils.doWithFields(clazz, new ReflectionUtils.FieldCallback() { @Override public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException { Created created = field.getAnnotation(Created.class); if (created != null) { config.setCreateField(field); } else { Modified modified = field.getAnnotation(Modified.class); if (modified != null) { config.setModifiedField(field); } } CreatedBy createdBy = field.getAnnotation(CreatedBy.class); if (createdBy != null) { config.setCreatedByField(field); } else { ModifiedBy modifiedBy = field.getAnnotation(ModifiedBy.class); if (modifiedBy != null) { config.setModifiedByField(field); } } // Version version = field.getAnnotation(Version.class); // if (version != null) { // config.setVersionFiled(field); // } } }); return config; }
@Override public Object plugin(Object target) { return Plugin.wrap(target, this); }
@Override public void setProperties(Properties properties) {
}
@Data private static class AuditingConfig {
private Field createField;
private Field modifiedField;
private Field createdByField;
private Field modifiedByField;
private Field versionFiled; }}文章分享
如果这篇文章对你有帮助,欢迎分享给更多人!
相关文章智能推荐
1
JMM
技术分享Java 内存模型详解。包含主内存/工作内存、happens-before、内存屏障、volatile 原理等。
2
JVM GC
技术分享JVM 垃圾回收机制详解。包含 GC 算法、垃圾回收器、内存区域、GC 日志分析等。
3
Playwright 使用心得
2025-08-08
4
Spring Cloud Gateway 流量复制
Spring Cloud Gateway 流量复制方案。包含复制场景、实现方式、注意事项等。
5
Drone CI 小试
技术分享每次推送代码到自建的 gitea 之后, 都要手动去 linux 服务器上执行部署脚本, 虽然这个操作一共也就几步(拉取最新代码 → 打包构建 → 生成 docker 镜像 → docker compose 运行), 之前就听说过 Drone CI, 这里假期刚好可以折腾小试一下.
随机文章随机推荐













