当前位置: 首页 > 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;代码的可读性很高。例如…...

铭豹扩展坞 USB转网口 突然无法识别解决方法

当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…...

Cesium1.95中高性能加载1500个点

一、基本方式&#xff1a; 图标使用.png比.svg性能要好 <template><div id"cesiumContainer"></div><div class"toolbar"><button id"resetButton">重新生成点</button><span id"countDisplay&qu…...

【Java_EE】Spring MVC

目录 Spring Web MVC ​编辑注解 RestController RequestMapping RequestParam RequestParam RequestBody PathVariable RequestPart 参数传递 注意事项 ​编辑参数重命名 RequestParam ​编辑​编辑传递集合 RequestParam 传递JSON数据 ​编辑RequestBody ​…...

数据库分批入库

今天在工作中&#xff0c;遇到一个问题&#xff0c;就是分批查询的时候&#xff0c;由于批次过大导致出现了一些问题&#xff0c;一下是问题描述和解决方案&#xff1a; 示例&#xff1a; // 假设已有数据列表 dataList 和 PreparedStatement pstmt int batchSize 1000; // …...

RSS 2025|从说明书学习复杂机器人操作任务:NUS邵林团队提出全新机器人装配技能学习框架Manual2Skill

视觉语言模型&#xff08;Vision-Language Models, VLMs&#xff09;&#xff0c;为真实环境中的机器人操作任务提供了极具潜力的解决方案。 尽管 VLMs 取得了显著进展&#xff0c;机器人仍难以胜任复杂的长时程任务&#xff08;如家具装配&#xff09;&#xff0c;主要受限于人…...

CRMEB 中 PHP 短信扩展开发:涵盖一号通、阿里云、腾讯云、创蓝

目前已有一号通短信、阿里云短信、腾讯云短信扩展 扩展入口文件 文件目录 crmeb\services\sms\Sms.php 默认驱动类型为&#xff1a;一号通 namespace crmeb\services\sms;use crmeb\basic\BaseManager; use crmeb\services\AccessTokenServeService; use crmeb\services\sms\…...

比较数据迁移后MySQL数据库和OceanBase数据仓库中的表

设计一个MySQL数据库和OceanBase数据仓库的表数据比较的详细程序流程,两张表是相同的结构,都有整型主键id字段,需要每次从数据库分批取得2000条数据,用于比较,比较操作的同时可以再取2000条数据,等上一次比较完成之后,开始比较,直到比较完所有的数据。比较操作需要比较…...

LabVIEW双光子成像系统技术

双光子成像技术的核心特性 双光子成像通过双低能量光子协同激发机制&#xff0c;展现出显著的技术优势&#xff1a; 深层组织穿透能力&#xff1a;适用于活体组织深度成像 高分辨率观测性能&#xff1a;满足微观结构的精细研究需求 低光毒性特点&#xff1a;减少对样本的损伤…...

为什么要创建 Vue 实例

核心原因:Vue 需要一个「控制中心」来驱动整个应用 你可以把 Vue 实例想象成你应用的**「大脑」或「引擎」。它负责协调模板、数据、逻辑和行为,将它们变成一个活的、可交互的应用**。没有这个实例,你的代码只是一堆静态的 HTML、JavaScript 变量和函数,无法「活」起来。 …...

如何应对敏捷转型中的团队阻力

应对敏捷转型中的团队阻力需要明确沟通敏捷转型目的、提升团队参与感、提供充分的培训与支持、逐步推进敏捷实践、建立清晰的奖励和反馈机制。其中&#xff0c;明确沟通敏捷转型目的尤为关键&#xff0c;团队成员只有清晰理解转型背后的原因和利益&#xff0c;才能降低对变化的…...