数据库公共字段自动填充的三种实现方案
背景介绍
在实际项目开发中,我们经常需要处理一些公共字段的自动填充,比如:
- createTime (创建时间)
- updateTime (更新时间)
- createUser (创建人)
- updateUser (更新人)
这些字段在每个表中都存在,如果每次都手动设置会很麻烦。下面介绍三种常用的解决方案。
方案一:MyBatis + AOP方式
这种方式通过自定义注解和切面来实现自动填充。
代码实现
- 添加
AOP
和MyBatis
依赖
SpringMVC
<!-- mybatis-spring -->
<dependency><groupId>org.mybatis</groupId><artifactId>mybatis-spring</artifactId><version>3.0.3</version>
</dependency>
<!-- AOP-spring -->
<dependency><groupId>org.springframework</groupId><artifactId>spring-aspects</artifactId><version>6.1.12</version></dependency>
SpringBoot
<!--mybatis起步依赖-->
<dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>3.0.3</version>
</dependency>
<!-- AOP -->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId>
</dependency>
- 自定义枚举类
/*** 数据库操作类型*/
public enum OperationType {UPDATE, //更新操作INSERT //插入操作}
- 自定义注解
@Target(ElementType.METHOD) // 作用于方法上
@Retention(RetentionPolicy.RUNTIME)
public @interface AutoFill {OperationType value(); // INSERT/UPDATE
}
- 切面类实现
@Aspect
@Component
@Slf4j
public class AutoFillAspect {/*** 切入点*/@Pointcut("execution(* com.sky.mapper.*.*(..)) && @annotation(com.sky.annotation.AutoFill)")public void autoFillPointcut() {}/*** 前置通知,在通知中进行公共字段的赋值*/@Before("autoFillPointcut()")public void autoFill(JoinPoint joinPoint) throws NoSuchMethodException {log.info("开始公共字段自动填充...");//获取当前被拦截的方法上的数据库操作字段MethodSignature signature = (MethodSignature) joinPoint.getSignature(); // 方法签名对象AutoFill autoFill = signature.getMethod().getAnnotation(AutoFill.class); // 获取方法上的注解对象OperationType value = autoFill.value(); // 获取数据库的操作类型值//获取当前被拦截的方法上的参数 -- 实体对象 (默认约定需要自动填充的方法,将实体对象放在第一个)Object[] args = joinPoint.getArgs();if (args == null || args.length == 0) {return;}Object entity = args[0];// 准备赋值的数据LocalDateTime now = LocalDateTime.now();Long currentId = BaseContext.getCurrentId();// 根据当前不同的操作类型,为对应的属性通过反射来赋值if (value == OperationType.INSERT){// 为4个公共字段赋值try {Method setCreateTime = entity.getClass().getDeclaredMethod("setCreateTime", LocalDateTime.class);Method setCreateUser = entity.getClass().getDeclaredMethod("setCreateUser", Long.class);Method setUpdateTime = entity.getClass().getDeclaredMethod("setUpdateTime", LocalDateTime.class);Method setUpdateUser = entity.getClass().getDeclaredMethod("setUpdateUser", Long.class);// 通过反射为对象属性赋值setCreateTime.invoke(entity, now);setCreateUser.invoke(entity, currentId);setUpdateTime.invoke(entity, now);setUpdateUser.invoke(entity, currentId);} catch (Exception e) {e.printStackTrace();}} else if (value == OperationType.UPDATE) {// 为4个公共字段赋值try {Method setUpdateTime = entity.getClass().getDeclaredMethod("setUpdateTime", LocalDateTime.class);Method setUpdateUser = entity.getClass().getDeclaredMethod("setUpdateUser", Long.class);// 通过反射为对象属性赋值setUpdateTime.invoke(entity, now);setUpdateUser.invoke(entity, currentId);} catch (Exception e) {e.printStackTrace();}}}}
- 定义常量类(可选)
其中"setCreateTime"
直接硬编码,容易出错,并且不够优雅,可以将其定义为常量类
/*** 公共字段自动填充相关常量*/
public class AutoFillConstant {/*** 实体类中的方法名称*/public static final String SET_CREATE_TIME = "setCreateTime";public static final String SET_UPDATE_TIME = "setUpdateTime";public static final String SET_CREATE_USER = "setCreateUser";public static final String SET_UPDATE_USER = "setUpdateUser";
}
- 在Mapper中对应需要进行公共字段自动填充的方法上加上注解
@AutoFill(value = OperationType.UPDATE)
void update(Category category)@AutoFill(value = OperationType.INSERT)
void insert(Category category)
优缺点
- 优点:
- 灵活性强,可自定义复杂的填充逻辑
- 可以统一管理所有需要自动填充的字段
- 缺点:
- 实现相对复杂
- 需要编写较多代码
- 需要手动添加注解
方案二:MyBatis-Plus方式
MyBatis-Plus
提供了更简便的实现方式。
代码实现
- 添加
AOP
和MP
依赖
SpringBoot3
<!--springboot2整合MybatisPlus-->
<!--MybatisPlus起步依赖-->
<dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.5.3.1</version>
</dependency><!--springboot3整合MybatisPlus-->
<!--MybatisPlus起步依赖-->
<dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-spring-boot3-starter</artifactId><version>3.5.5</version>
</dependency><!-- Spring Boot AOP依赖 -->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId>
</dependency>
- 实现
MetaObjectHandler
接口
需要定义一个类,需要实现 MetaObjectHandler
,然后实现里面的 insertFill()
和 updateFill()
方法,分别代表在执行插入操作和更新操作时执行对应的方法,而方法内部去实现你需要注入的字段的值
@Component
@Slf4j
public class MyMetaObjectHandler implements MetaObjectHandler {@Overridepublic void insertFill(MetaObject metaObject) {log.info("start insert fill ....");// 设置插入时的字段this.setFieldValByName("createTime", LocalDateTime.now(), metaObject);this.setFieldValByName("updateTime", LocalDateTime.now(), metaObject);this.setFieldValByName("createUser", BaseContext.getCurrentId(), metaObject);this.setFieldValByName("updateUser", BaseContext.getCurrentId(), metaObject);}@Overridepublic void updateFill(MetaObject metaObject) {log.info("start update fill ....");// 设置更新时的字段this.setFieldValByName("updateTime", LocalDateTime.now(), metaObject);this.setFieldValByName("updateUser", BaseContext.getCurrentId(), metaObject);}
}
- 实体类字段添加
@TableField
注解
@TableField(fill = FieldFill.INSERT) // 执行插入时自动填充
private LocalDateTime createTime;@TableField(fill = FieldFill.INSERT)
private Long createUser;@TableField(fill = FieldFill.INSERT_UPDATE) // 执行插入和更新时自动填充
private LocalDateTime updateTime;@TableField(fill = FieldFill.INSERT_UPDATE)
private Long updateUser;
@TableField(fill = FieldFill.INSERT)
: 执行插入时自动填充
@TableField(fill = FieldFill.INSERT_UPDATE)
: 执行插入和更新时自动填充
还有其他的属性,可以根据业务需求自行添加
优缺点
- 优点:
- 使用简单,代码量少
- 开箱即用,无需复杂配置
- 与MyBatis-Plus无缝集成
- 缺点:
- 填充逻辑相对固定
- 扩展性较差
方案三:数据库默认值
直接在数据库表设计时设置默认值。
SQL实现
CREATE TABLE `table_name` (`id` bigint NOT NULL AUTO_INCREMENT,`create_time` datetime DEFAULT CURRENT_TIMESTAMP,`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,PRIMARY KEY (`id`)
) ENGINE=InnoDB;
优缺点
- 优点:
- 实现最简单
- 不需要额外代码
- 数据库层面保证字段有值
- 缺点:
- 只能处理简单的默认值场景
- 无法获取当前登录用户等业务信息
- 不同数据库实现可能不一样
方案选择建议
- 对于简单的时间字段(createTime/updateTime):
- 建议使用数据库默认值
- 可以保证数据的一致性
- 需要记录操作人等业务字段:
- 推荐使用MyBatis-Plus方式
- 简单高效,满足大部分需求
- 有特殊业务逻辑:
- 考虑使用AOP方式
- 可以实现更复杂的填充逻辑
- 实际项目中可以组合使用:
- 时间字段用数据库默认值
- 业务字段用MyBatis-Plus或AOP
总结
- 三种方案各有优劣,需要根据实际需求选择
- 推荐优先使用MyBatis-Plus方式,简单且功能足够
- 特殊场景再考虑其他方案
- 可以组合使用不同方案,扬长避短
相关文章:

数据库公共字段自动填充的三种实现方案
背景介绍 在实际项目开发中,我们经常需要处理一些公共字段的自动填充,比如: createTime (创建时间)updateTime (更新时间)createUser (创建人)updateUser (更新人) 这些字段在每个表中都存在,如果每次都手动设置会很麻烦。下面介绍三种常用的解决方案。 方案一:M…...
《MySQL 入门:数据库世界的第一扇门》
一、MySQL 简介 MySQL 是一种开源的关系型数据库管理系统,在数据库领域占据着重要地位。它以其高效查询、高安全性、低成本和扩展性著称,广泛应用于网站、企业级应用、数据分析等领域。 MySQL 具有诸多优点。首先,它成本低,作为…...

Qt之第三方库QCustomPlot使用(二)
Qt开发 系列文章 - qcustomplot(二) 目录 前言 一、Qt开源库 二、QCustomPlot 1.qcustomplot介绍 2.qcustomplot下载 3.qcustomplot移植 4.修改项目文件.pro 5.提升QWidget类 三、技巧讲解 1.拖动缩放功能 2.等待更新 总结 前言 Qt第三方…...
JAVA-类与继承
啥是继承? 在JAVA中, 继承就是子类继承父类的特征和行为,使得子类拥有父类的特征和行为,同时还可以拥有父类所没有的特征和行为。 举个例子通俗来讲,兔子和羊是食草动物类,狮子和豹子是食肉动物类&#x…...
SSH连接报错,Corrupted MAC on input 解决方法
问题描述 客户在windows CMD中SSH连接失败,报错: Corrupted MAC on input ssh_dispatch_run_fatal: Connection to x.x.x.x port 22: message authentication code incorrect值得注意的是,客户通过别的机器做SSH连接可以成功,使用putty, mo…...
【C++】8___继承
目录 一、基本语法 二、继承方式 三、对象模型 四、继承中的构造与析构的顺序 五、继承中同名成员处理 六、多继承语法 七、菱形继承 一、基本语法 好处:减少重复的代码 语法: class 子类 : 继承方式 父类 子类 也称为 派生类 父类…...
C# 中的异常处理:构建健壮和可靠的程序
C#中的异常处理(Exception Handling)。异常处理是编程中非常重要的一部分,它允许开发者优雅地处理程序运行时可能出现的错误或意外情况。通过有效的异常处理,可以使应用程序更加健壮、可靠,并提供更好的用户体验。以下…...

基于智能合约的医院凭证共享中心路径探析
一、引言 随着医疗行业的不断发展和信息技术的进步,基于智能合约的医疗凭证共享中心解决方案成为了可能。在当今数字化时代,医疗领域面临着诸多挑战,如医疗数据的分散存储、信息共享的不便捷以及凭证管理的复杂性等问题。而智能合约的出现&am…...
vba学习系列(9)--按需求计数单元格数量
系列文章目录 文章目录 系列文章目录前言一、按需求计数单元格数量1.需求 二、使用步骤1.vba源码2.整理后 总结 前言 一、按需求计数单元格数量 1.需求 一个表中有多个类型的单元格内容,比如:文字、数字、特殊字符、字母数字…… 我们要计数字母数字的…...

scale index的计算
scale index定义 基本实现 需要注意,scale index的提出者分别构建了MATLAB和R语言的实现方式。 但是,需要注意,经过我向作者求证。 MATLAB编写的代码已经“过时了”,为了拥抱时代,作者构建了R语言包,名称为…...
鸿蒙实现Web组件开发
目录: 1、简介&使用场景2、加载网络页面3、加载本地页面4、加载HTML格式的文本数据5、设置深色模式6、上传文件7、在新窗口中打开页面8、管理位置权限 1、简介&使用场景 Web是一种基于互联网的技术和资源的网络服务系统。它是指由许多互连的计算机组成的全…...

Linux——linux系统移植
创建VSCode工程 1、将NXP官方的linux内核拷贝到Ubuntu 2、解压缩tar -vxjf linux-imx-rel_imx_4.1.15_2.1.0_ga.tar.bz2 NXP官方开发板Linux内核编译 1、将.vscode文件夹复制到NXP官网linux工程中,屏蔽一些不需要的文件 2、编译NXP官方EVK开发板对应的Linux系统…...
工业摄像头应对复杂环境的策略与解决方案
工业摄像头需应对复杂环境,如极端温度、振动、尘土、光照不足等。为确保稳定工作,它采用了先进技术和设计。详细分析如下: 一、增强环境适应性 采用高灵敏度传感器:使用CMOS或CCD图像传感器,适应低光照条件。 高精度、…...

重生之我在异世界学编程之C语言:深入动态内存管理篇
大家好,这里是小编的博客频道 小编的博客:就爱学编程 很高兴在CSDN这个大家庭与大家相识,希望能在这里与大家共同进步,共同收获更好的自己!!! 本文目录 引言正文一 动态内存管理的必要性二 动态…...

【经典论文阅读】Latent Diffusion Models(LDM)
Latent Diffusion Models High-Resolution Image Synthesis with Latent Diffusion Models 摘要 动机:在有限的计算资源下进行扩散模型训练,同时保持质量和灵活性 引入跨注意力层,以卷积方式实现对一般条件输入(如文本或边界框…...
智能指针中的weak_ptr(弱引用智能指针)
弱引用智能指针 std::weak_ptr 可以看做是shared_ptr的助手,它不管理 shared_ptr 内部的指针。std::weak_ptr 没有重载操作符*和->,因为它不共享指针, 不能操作资源,所以它的构造不会增加引用计数,析构也不会减少引用计数,它的…...

【电子通识】机电继电器和固态继电器的区别
机电继电器 机电继电器于19世纪中叶发明。这些器件将线圈与可移动的金属触点结合使用来充当电动开关。这些器件会因为金属触点出现磨损而发生故障,例如焊死在一起。因此,在完全失效之前器件能够进行的开关周期数有限,从而限制了其总体可靠性。 一般情况下继电器控制…...

工业异常检测-CVPR2024-新的3D异常数据合成办法和自监督网络IMRNet
论文:https://arxiv.org/pdf/2311.14897v3.pdf 项目:https://github.com/chopper-233/anomaly-shapenet 这篇论文主要关注的是3D异常检测和定位,这是一个在工业质量检查中至关重要的任务。作者们提出了一种新的方法来合成3D异常数据&#x…...

如何创建对话窗口
文章目录 1. 概念介绍2. 使用方法3. 示例代码我们在上一章回中介绍了Dismissible Widget相关的内容,本章回中将介绍AlertDialog Widget.闲话休提,让我们一起Talk Flutter吧。 1. 概念介绍 我们介绍的AlertDialog是指程序中弹出的确认窗口,其实我们在上一章回中删除ListView中…...

新手上路,学Go还是Python
对于新手来说,Go和Python都是很好的编程语言,它们各有特点,以下是详细的对比来帮助你决定先学哪一个: 一、语法和学习难度 Python 语法简洁易懂:Python以其简洁、优雅的语法而闻名,代码的可读性很高。例如…...

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式
一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明:假设每台服务器已…...
Python|GIF 解析与构建(5):手搓截屏和帧率控制
目录 Python|GIF 解析与构建(5):手搓截屏和帧率控制 一、引言 二、技术实现:手搓截屏模块 2.1 核心原理 2.2 代码解析:ScreenshotData类 2.2.1 截图函数:capture_screen 三、技术实现&…...

Vue3 + Element Plus + TypeScript中el-transfer穿梭框组件使用详解及示例
使用详解 Element Plus 的 el-transfer 组件是一个强大的穿梭框组件,常用于在两个集合之间进行数据转移,如权限分配、数据选择等场景。下面我将详细介绍其用法并提供一个完整示例。 核心特性与用法 基本属性 v-model:绑定右侧列表的值&…...
QMC5883L的驱动
简介 本篇文章的代码已经上传到了github上面,开源代码 作为一个电子罗盘模块,我们可以通过I2C从中获取偏航角yaw,相对于六轴陀螺仪的yaw,qmc5883l几乎不会零飘并且成本较低。 参考资料 QMC5883L磁场传感器驱动 QMC5883L磁力计…...

centos 7 部署awstats 网站访问检测
一、基础环境准备(两种安装方式都要做) bash # 安装必要依赖 yum install -y httpd perl mod_perl perl-Time-HiRes perl-DateTime systemctl enable httpd # 设置 Apache 开机自启 systemctl start httpd # 启动 Apache二、安装 AWStats࿰…...

vscode(仍待补充)
写于2025 6.9 主包将加入vscode这个更权威的圈子 vscode的基本使用 侧边栏 vscode还能连接ssh? debug时使用的launch文件 1.task.json {"tasks": [{"type": "cppbuild","label": "C/C: gcc.exe 生成活动文件"…...

SpringCloudGateway 自定义局部过滤器
场景: 将所有请求转化为同一路径请求(方便穿网配置)在请求头内标识原来路径,然后在将请求分发给不同服务 AllToOneGatewayFilterFactory import lombok.Getter; import lombok.Setter; import lombok.extern.slf4j.Slf4j; impor…...
#Uniapp篇:chrome调试unapp适配
chrome调试设备----使用Android模拟机开发调试移动端页面 Chrome://inspect/#devices MuMu模拟器Edge浏览器:Android原生APP嵌入的H5页面元素定位 chrome://inspect/#devices uniapp单位适配 根路径下 postcss.config.js 需要装这些插件 “postcss”: “^8.5.…...

短视频矩阵系统文案创作功能开发实践,定制化开发
在短视频行业迅猛发展的当下,企业和个人创作者为了扩大影响力、提升传播效果,纷纷采用短视频矩阵运营策略,同时管理多个平台、多个账号的内容发布。然而,频繁的文案创作需求让运营者疲于应对,如何高效产出高质量文案成…...
嵌入式常见 CPU 架构
架构类型架构厂商芯片厂商典型芯片特点与应用场景PICRISC (8/16 位)MicrochipMicrochipPIC16F877A、PIC18F4550简化指令集,单周期执行;低功耗、CIP 独立外设;用于家电、小电机控制、安防面板等嵌入式场景8051CISC (8 位)Intel(原始…...