当前位置: 首页 > news >正文

数据库公共字段自动填充的三种实现方案

背景介绍

在实际项目开发中,我们经常需要处理一些公共字段自动填充,比如:

  • createTime (创建时间)
  • updateTime (更新时间)
  • createUser (创建人)
  • updateUser (更新人)
    这些字段在每个表中都存在,如果每次都手动设置会很麻烦。下面介绍三种常用的解决方案。

方案一:MyBatis + AOP方式

这种方式通过自定义注解和切面来实现自动填充。

代码实现

  1. 添加AOPMyBatis依赖

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>
  1. 自定义枚举类
/*** 数据库操作类型*/
public enum OperationType {UPDATE,  //更新操作INSERT   //插入操作}
  1. 自定义注解
@Target(ElementType.METHOD)  // 作用于方法上
@Retention(RetentionPolicy.RUNTIME)
public @interface AutoFill {OperationType value();  // INSERT/UPDATE
}
  1. 切面类实现
@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();}}}}
  1. 定义常量类(可选)
    其中"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";
}
  1. 在Mapper中对应需要进行公共字段自动填充的方法上加上注解
@AutoFill(value = OperationType.UPDATE)
void update(Category category)@AutoFill(value = OperationType.INSERT)
void insert(Category category)

优缺点

  1. 优点:
    • 灵活性强,可自定义复杂的填充逻辑
    • 可以统一管理所有需要自动填充的字段
  2. 缺点:
    • 实现相对复杂
    • 需要编写较多代码
    • 需要手动添加注解

方案二:MyBatis-Plus方式

MyBatis-Plus提供了更简便的实现方式。

代码实现

  1. 添加AOPMP依赖

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>
  1. 实现 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);}
} 
  1. 实体类字段添加@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) : 执行插入和更新时自动填充
还有其他的属性,可以根据业务需求自行添加
在这里插入图片描述

优缺点

  1. 优点:
    • 使用简单,代码量少
    • 开箱即用,无需复杂配置
    • 与MyBatis-Plus无缝集成
  2. 缺点:
    • 填充逻辑相对固定
    • 扩展性较差

方案三:数据库默认值

直接在数据库表设计时设置默认值。

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;

优缺点

  1. 优点:
    • 实现最简单
    • 不需要额外代码
    • 数据库层面保证字段有值
  2. 缺点:
    • 只能处理简单的默认值场景
    • 无法获取当前登录用户等业务信息
    • 不同数据库实现可能不一样

方案选择建议

  1. 对于简单的时间字段(createTime/updateTime):
    • 建议使用数据库默认值
    • 可以保证数据的一致性
  2. 需要记录操作人等业务字段:
    • 推荐使用MyBatis-Plus方式
    • 简单高效,满足大部分需求
  3. 有特殊业务逻辑:
    • 考虑使用AOP方式
    • 可以实现更复杂的填充逻辑
  4. 实际项目中可以组合使用:
    • 时间字段用数据库默认值
    • 业务字段用MyBatis-Plus或AOP

总结

  1. 三种方案各有优劣,需要根据实际需求选择
  2. 推荐优先使用MyBatis-Plus方式,简单且功能足够
  3. 特殊场景再考虑其他方案
  4. 可以组合使用不同方案,扬长避短

相关文章:

数据库公共字段自动填充的三种实现方案

背景介绍 在实际项目开发中,我们经常需要处理一些公共字段的自动填充,比如: createTime (创建时间)updateTime (更新时间)createUser (创建人)updateUser (更新人) 这些字段在每个表中都存在,如果每次都手动设置会很麻烦。下面介绍三种常用的解决方案。 方案一&#xff1a;M…...

《MySQL 入门:数据库世界的第一扇门》

一、MySQL 简介 MySQL 是一种开源的关系型数据库管理系统&#xff0c;在数据库领域占据着重要地位。它以其高效查询、高安全性、低成本和扩展性著称&#xff0c;广泛应用于网站、企业级应用、数据分析等领域。 MySQL 具有诸多优点。首先&#xff0c;它成本低&#xff0c;作为…...

Qt之第三方库QCustomPlot使用(二)

Qt开发 系列文章 - qcustomplot&#xff08;二&#xff09; 目录 前言 一、Qt开源库 二、QCustomPlot 1.qcustomplot介绍 2.qcustomplot下载 3.qcustomplot移植 4.修改项目文件.pro 5.提升QWidget类‌ 三、技巧讲解 1.拖动缩放功能 2.等待更新 总结 前言 Qt第三方…...

JAVA-类与继承

啥是继承&#xff1f; 在JAVA中&#xff0c; 继承就是子类继承父类的特征和行为&#xff0c;使得子类拥有父类的特征和行为&#xff0c;同时还可以拥有父类所没有的特征和行为。 举个例子通俗来讲&#xff0c;兔子和羊是食草动物类&#xff0c;狮子和豹子是食肉动物类&#x…...

SSH连接报错,Corrupted MAC on input 解决方法

问题描述 客户在windows CMD中SSH连接失败&#xff0c;报错: Corrupted MAC on input ssh_dispatch_run_fatal: Connection to x.x.x.x port 22: message authentication code incorrect值得注意的是&#xff0c;客户通过别的机器做SSH连接可以成功&#xff0c;使用putty, mo…...

【C++】8___继承

目录 一、基本语法 二、继承方式 三、对象模型 四、继承中的构造与析构的顺序 五、继承中同名成员处理 六、多继承语法 七、菱形继承 一、基本语法 好处&#xff1a;减少重复的代码 语法&#xff1a; class 子类 &#xff1a; 继承方式 父类 子类 也称为 派生类 父类…...

C# 中的异常处理:构建健壮和可靠的程序

C#中的异常处理&#xff08;Exception Handling&#xff09;。异常处理是编程中非常重要的一部分&#xff0c;它允许开发者优雅地处理程序运行时可能出现的错误或意外情况。通过有效的异常处理&#xff0c;可以使应用程序更加健壮、可靠&#xff0c;并提供更好的用户体验。以下…...

基于智能合约的医院凭证共享中心路径探析

一、引言 随着医疗行业的不断发展和信息技术的进步&#xff0c;基于智能合约的医疗凭证共享中心解决方案成为了可能。在当今数字化时代&#xff0c;医疗领域面临着诸多挑战&#xff0c;如医疗数据的分散存储、信息共享的不便捷以及凭证管理的复杂性等问题。而智能合约的出现&am…...

vba学习系列(9)--按需求计数单元格数量

系列文章目录 文章目录 系列文章目录前言一、按需求计数单元格数量1.需求 二、使用步骤1.vba源码2.整理后 总结 前言 一、按需求计数单元格数量 1.需求 一个表中有多个类型的单元格内容&#xff0c;比如&#xff1a;文字、数字、特殊字符、字母数字…… 我们要计数字母数字的…...

scale index的计算

scale index定义 基本实现 需要注意&#xff0c;scale index的提出者分别构建了MATLAB和R语言的实现方式。 但是&#xff0c;需要注意&#xff0c;经过我向作者求证。 MATLAB编写的代码已经“过时了”&#xff0c;为了拥抱时代&#xff0c;作者构建了R语言包&#xff0c;名称为…...

鸿蒙实现Web组件开发

目录&#xff1a; 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工程中&#xff0c;屏蔽一些不需要的文件 2、编译NXP官方EVK开发板对应的Linux系统…...

工业摄像头应对复杂环境的策略与解决方案

工业摄像头需应对复杂环境&#xff0c;如极端温度、振动、尘土、光照不足等。为确保稳定工作&#xff0c;它采用了先进技术和设计。详细分析如下&#xff1a; 一、增强环境适应性 采用高灵敏度传感器&#xff1a;使用CMOS或CCD图像传感器&#xff0c;适应低光照条件。 高精度、…...

重生之我在异世界学编程之C语言:深入动态内存管理篇

大家好&#xff0c;这里是小编的博客频道 小编的博客&#xff1a;就爱学编程 很高兴在CSDN这个大家庭与大家相识&#xff0c;希望能在这里与大家共同进步&#xff0c;共同收获更好的自己&#xff01;&#xff01;&#xff01; 本文目录 引言正文一 动态内存管理的必要性二 动态…...

【经典论文阅读】Latent Diffusion Models(LDM)

Latent Diffusion Models High-Resolution Image Synthesis with Latent Diffusion Models 摘要 动机&#xff1a;在有限的计算资源下进行扩散模型训练&#xff0c;同时保持质量和灵活性 引入跨注意力层&#xff0c;以卷积方式实现对一般条件输入&#xff08;如文本或边界框…...

智能指针中的weak_ptr(弱引用智能指针)

弱引用智能指针 std::weak_ptr 可以看做是shared_ptr的助手,它不管理 shared_ptr 内部的指针。std::weak_ptr 没有重载操作符*和->&#xff0c;因为它不共享指针&#xff0c; 不能操作资源&#xff0c;所以它的构造不会增加引用计数&#xff0c;析构也不会减少引用计数,它的…...

【电子通识】机电继电器和固态继电器的区别

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

工业异常检测-CVPR2024-新的3D异常数据合成办法和自监督网络IMRNet

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

如何创建对话窗口

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

新手上路,学Go还是Python

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

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式

一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明&#xff1a;假设每台服务器已…...

Python|GIF 解析与构建(5):手搓截屏和帧率控制

目录 Python&#xff5c;GIF 解析与构建&#xff08;5&#xff09;&#xff1a;手搓截屏和帧率控制 一、引言 二、技术实现&#xff1a;手搓截屏模块 2.1 核心原理 2.2 代码解析&#xff1a;ScreenshotData类 2.2.1 截图函数&#xff1a;capture_screen 三、技术实现&…...

Vue3 + Element Plus + TypeScript中el-transfer穿梭框组件使用详解及示例

使用详解 Element Plus 的 el-transfer 组件是一个强大的穿梭框组件&#xff0c;常用于在两个集合之间进行数据转移&#xff0c;如权限分配、数据选择等场景。下面我将详细介绍其用法并提供一个完整示例。 核心特性与用法 基本属性 v-model&#xff1a;绑定右侧列表的值&…...

QMC5883L的驱动

简介 本篇文章的代码已经上传到了github上面&#xff0c;开源代码 作为一个电子罗盘模块&#xff0c;我们可以通过I2C从中获取偏航角yaw&#xff0c;相对于六轴陀螺仪的yaw&#xff0c;qmc5883l几乎不会零飘并且成本较低。 参考资料 QMC5883L磁场传感器驱动 QMC5883L磁力计…...

centos 7 部署awstats 网站访问检测

一、基础环境准备&#xff08;两种安装方式都要做&#xff09; bash # 安装必要依赖 yum install -y httpd perl mod_perl perl-Time-HiRes perl-DateTime systemctl enable httpd # 设置 Apache 开机自启 systemctl start httpd # 启动 Apache二、安装 AWStats&#xff0…...

vscode(仍待补充)

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

SpringCloudGateway 自定义局部过滤器

场景&#xff1a; 将所有请求转化为同一路径请求&#xff08;方便穿网配置&#xff09;在请求头内标识原来路径&#xff0c;然后在将请求分发给不同服务 AllToOneGatewayFilterFactory import lombok.Getter; import lombok.Setter; import lombok.extern.slf4j.Slf4j; impor…...

#Uniapp篇:chrome调试unapp适配

chrome调试设备----使用Android模拟机开发调试移动端页面 Chrome://inspect/#devices MuMu模拟器Edge浏览器&#xff1a;Android原生APP嵌入的H5页面元素定位 chrome://inspect/#devices uniapp单位适配 根路径下 postcss.config.js 需要装这些插件 “postcss”: “^8.5.…...

短视频矩阵系统文案创作功能开发实践,定制化开发

在短视频行业迅猛发展的当下&#xff0c;企业和个人创作者为了扩大影响力、提升传播效果&#xff0c;纷纷采用短视频矩阵运营策略&#xff0c;同时管理多个平台、多个账号的内容发布。然而&#xff0c;频繁的文案创作需求让运营者疲于应对&#xff0c;如何高效产出高质量文案成…...

嵌入式常见 CPU 架构

架构类型架构厂商芯片厂商典型芯片特点与应用场景PICRISC (8/16 位)MicrochipMicrochipPIC16F877A、PIC18F4550简化指令集&#xff0c;单周期执行&#xff1b;低功耗、CIP 独立外设&#xff1b;用于家电、小电机控制、安防面板等嵌入式场景8051CISC (8 位)Intel&#xff08;原始…...