Springboot中基于注解实现公共字段自动填充
1.使用场景
当我们有大量的表需要管理公共字段,并且希望提高开发效率和确保数据一致性时,使用这种自动填充方式是很有必要的。它可以达到一下作用
-
统一管理数据库表中的公共字段:如创建时间、修改时间、创建人ID、修改人ID等,这些字段在所有表中都会存在,需要在插入和更新时保持一致性。
-
避免重复代码:在不同的业务逻辑中频繁操作相同的字段(如每次插入记录时手动设置创建时间和创建人),使用自动填充可以避免手动填写这些字段,减少重复代码。
-
提高开发效率:当项目中有大量类似的插入或更新操作时,通过自动填充机制可以减轻手动维护字段的负担,提高开发效率和代码的可维护性。
-
确保数据的准确性:通过切面统一处理公共字段的填充,减少人为错误,确保创建时间、修改时间等字段始终准确。
2.步骤(以商城数据维护项目为例)
2.1 定义一个枚举类 OperationType,
这个类用于表示数据库操作的类型。枚举包含两个常量:
- UPDATE:表示更新操作。
- INSERT:表示插入操作。
枚举类通常用于标识某个操作是插入还是更新操作,方便在代码中进行逻辑判断或执行特定操作(如公共字段的自动填充),来让我在不同的数据库操作类型中统一处理。
package com.sky.enumeration;/*** 数据库操作类型*/
public enum OperationType {/*** 更新操作*/UPDATE,/*** 插入操作*/INSERT
}
2.2 定义名为 AutoFill 的自定义注解
自定义注解用于标识某个方法需要进行公共字段的自动填充。注解包含一个刚才定义 OperationType 枚举值,用于指定该方法对应的数据库操作类型,是插入还是更新。
我下面对重要内容进行解释
- @Target(ElementType.METHOD):标注该注解只能应用在方法上。
- @Retention(RetentionPolicy.RUNTIME):指定该注解在运行时保留,可以通过反射机制获取该注解。
- OperationType value():这个方法定义了注解的一个属性,必须设置
OperationType枚举值(INSERT或UPDATE),来表示操作类型。
package com.sky.annotation;import com.sky.enumeration.OperationType;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/*** 自定义注解,用于标识某个方法需要进行功能字段自动填充*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AutoFill {// 数据库操作类型:UPDATE 或 INSERTOperationType value();
}
这个自定义注解的主要作用是在特定的数据库操作方法上使用,通过指定操作类型(插入或更新),结合其他逻辑实现公共字段的自动填充。
2.3 定义名为 AutoFillAspect 的切面类
这个类用于处理带有 @AutoFill 注解的方法,通过定义切面,切入点,前置通知实现
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import lombok.extern.slf4j.Slf4j;/*** 自定义切面,实现公共字段自动填充处理逻辑*/
@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){log.info("开始进行公共字段自动填充...");}
}
- @Aspect:声明该类是一个切面类,负责切入业务逻辑并执行相应操作。
- @Component:将这个类注册为Spring的组件,以便在应用程序中自动管理和注入。
- @Slf4j:启用日志记录功能,方便在切面逻辑中记录日志(当前代码中没有具体使用日志)。
- @Pointcut:定义切入点,拦截我
com.sky.mapper包下所有带有@AutoFill注解的方法。即拦截那些需要自动填充公共字段的数据库操作方法。 - @Before("autoFillPointCut()"):表示该方法会在切入点方法执行之前执行,这里切入点是那些符合
autoFillPointCut定义的带有@AutoFill注解的方法。 - JoinPoint 参数:
JoinPoint是AOP的一个接口,允许在切面中获取被拦截方法的相关信息(如方法名、参数等),便于进行逻辑处理。
那么当我们此时定义好切面后,对于我们自定义的这个auto fill这个注解,需要把它加到我们XXX Mapper的mybatis文件上面。
2.4 插入数据的Mapper方法(我这边以员工数据维护为例)
在我们特定数据库操作(Insert,Update)上使用了@AutoFill注解,指定了操作类型为OperationType.INSERT(UPDATE),意味着该方法会在插入数据或者更新数据时自动填充创建时间和创建人等公共字段。
//INSERT
@Insert("insert into employee (name, username, password, phone, sex, id_number, create_time, update_time, create_user, update_user) " +"values (#{name}, #{username}, #{password}, #{phone}, #{sex}, #{idNumber}, #{createTime}, #{updateTime}, #{createUser}, #{updateUser})")
@AutoFill(value = OperationType.INSERT)
void insert(Employee employee);//UPDATE
@AutoFill(value = OperationType.UPDATE)
void update(Employee employee);
这边当调用 insert(Employee employee) 方法时,SQL插入语句将会执行,并且在插入之前,通过上面自定义 @AutoFill 注解的拦截逻辑自动为 create_time 和 create_user 字段赋值。
2.5 切面类中autoFill方法逻辑完成
在上面2.3步骤中我们只是对下面方法进行Log输入,在这个步骤进一步完善通知中逻辑,下面的步骤都是在此方法中实现
@Before("autoFillPointCut()")public void autoFill(JoinPoint joinPoint){log.info("开始进行公共字段自动填充...");}
2.5.1 需要获取 @AutoFill 注解的值,以判断当前操作是插入还是更新。
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
AutoFill autoFill = signature.getMethod().getAnnotation(AutoFill.class);
OperationType operationType = autoFill.value();
MethodSignature signature = (MethodSignature) joinPoint.getSignature():通过JoinPoint获取方法签名。AutoFill autoFill = signature.getMethod().getAnnotation(AutoFill.class):通过反射方法签名获取@AutoFill注解。OperationType operationType = autoFill.value():从注解中获取操作类型,判断是INSERT还是UPDATE。
2.5.2 获取实体对象
接下来获取当前方法的参数,因为实体对象是作为方法参数传入的。
Object[] args = joinPoint.getArgs();
if (args == null || args.length == 0) {return;
}
Object entity = args[0];
Object[] args = joinPoint.getArgs():获取当前方法的参数列表。通常参数的第一个就是实体对象。Object entity = args[0]:假设实体对象是第一个参数,将其提取出来进行后续的字段赋值操作。
2.5.3 准备数据
为了填充公共字段,首先需要获取当前时间和当前用户ID。通常这些信息是从上下文环境或安全机制中获取的。
LocalDateTime now = LocalDateTime.now();
Long currentId = BaseContext.getCurrentId();
LocalDateTime now = LocalDateTime.now():获取当前时间,用于设置createTime和updateTime。Long currentId = BaseContext.getCurrentId():通过BaseContext获取当前操作的用户ID,用于设置createUser和updateUser。
2.5.3 根据操作类型自动填充字段
如果操作类型是 INSERT,那么需要为四个字段赋值:createTime、createUser、updateTime 和 updateUser。
if (operationType == OperationType.INSERT) {try {Method setCreateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_CREATE_TIME, LocalDateTime.class);Method setCreateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_CREATE_USER, Long.class);Method setUpdateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class);Method setUpdateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_USER, Long.class);setCreateTime.invoke(entity, now);setCreateUser.invoke(entity, currentId);setUpdateTime.invoke(entity, now);setUpdateUser.invoke(entity, currentId);} catch (Exception e) {throw new RuntimeException(e);}
}
Method setCreateTime = entity.getClass().getDeclaredMethod(...):通过反射机制,获取实体对象的setCreateTime方法。setCreateTime.invoke(entity, now):通过反射调用setCreateTime方法,将当前时间now赋值给createTime字段。这里使用反射动态调用实体类中的set方法为指定字段赋值,从而实现灵活的字段处理。- 对
setCreateUser、setUpdateTime、setUpdateUser也执行相同操作,分别赋值给实体的createUser、updateTime和updateUser字段。
如果操作类型是 UPDATE,则只需要为 updateTime 和 updateUser 两个字段赋值。
else if (operationType == operationType.UPDATE) {try {Method setUpdateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class);Method setUpdateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_USER, Long.class);setUpdateTime.invoke(entity, now);setUpdateUser.invoke(entity, currentId);} catch (Exception e) {throw new RuntimeException(e);}
}
- 通过反射获取
setUpdateTime和setUpdateUser方法,并分别为updateTime和updateUser字段赋值。 - 更新操作不需要设置
createTime和createUser,因为这些字段只在插入时赋值。
总结:通过以上步骤实现公共字段填充,这样做其实是为了简化代码,减少重复操作,确保创建和修改相关字段在插入和更新操作中自动且一致地被赋值。
相关文章:
Springboot中基于注解实现公共字段自动填充
1.使用场景 当我们有大量的表需要管理公共字段,并且希望提高开发效率和确保数据一致性时,使用这种自动填充方式是很有必要的。它可以达到一下作用 统一管理数据库表中的公共字段:如创建时间、修改时间、创建人ID、修改人ID等,这些…...
Android 已经过时的方法用什么新方法替代?
过时修正举例 (Kotlin): getColor(): resources.getColor(R.color.white) //已过时// 修正后:ContextCompat.getColor(this, R.color.white) getDrawable(): resources.getDrawable(R.mipmap.test) //已过时//修正后:ContextCompat.getDrawable(this, R.mipmap.test) //…...
【RocketMQ】MQ与RocketMQ介绍
🎯 导读:本文介绍了消息队列(MQ)的基本概念及其在分布式系统中的作用,包括实现异步通信、削峰限流和应用解耦等方面的优势,并对ActiveMQ、RabbitMQ、RocketMQ及Kafka四种MQ产品进行了对比分析,涵…...
【笔记】自动驾驶预测与决策规划_Part4_时空联合规划
文章目录 0. 前言1. 时空联合规划的基本概念1.1 时空分离方法1.2 时空联合方法 2.基于搜索的时空联合规划 (Hybrid A* )2.1 基于Hybrid A* 的时空联合规划建模2.2 构建三维时空联合地图2.3 基于Hybrid A*的时空节点扩展2.4 Hybrid A* :时空节…...
Linux指令收集
文件和目录操作 ls: 列出目录内容。 -l 显示详细信息。-a 显示隐藏文件(以.开头的文件)。cd: 改变当前工作目录。 cd ~ 返回主目录。cd .. 上移一级目录。pwd: 显示当前工作目录。mkdir: 创建目录。 mkdir -p path/to/directory 创建多级目录。rmdir: 删…...
《C++并发编程实战》笔记(五)
五、内存模型和原子操作 5.1 C中的标准原子类型 原子操作是不可分割的操作,它或者完全做好,或者完全没做。 标准原子类型的定义在头文件<atomic>中,类模板std::atomic<T>接受各种类型的模板实参,从而创建该类型对应…...
在Python中实现多目标优化问题(5)
在Python中实现多目标优化问题 在Python中实现多目标优化,除了传统的进化算法(如NSGA-II、MOEA/D)和机器学习辅助的方法之外,还有一些新的方法和技术。以下是一些较新的或较少被提及的方法: 1. 基于梯度的多目标优化…...
【Linux:共享内存】
共享内存的概念: 操作系统通过页表将共享内存的起始虚拟地址映射到当前进程的地址空间中共享内存是由需要通信的双方进程之一来创建但该资源并不属于创建它的进程,而属于操作系统 共享内存可以在系统中存在多份,供不同个数,不同进…...
今年Java回暖了吗
今年回暖了吗 仅结合师兄和同学的情况 BG 大多双非本 少部分211本 985硕 去年十月一之前 基本转正都失败 十月一之前0 offer 只有很少的人拿到美团 今年十月一之前 有HC的基本都转正了(美团、字节等),目前没有HC的说也有机会(…...
a = Sw,其中a和w是向量,S是矩阵,求w等于什么?w可以写成关于a和S的什么样子的公式
给定公式: a S w a S w aSw 其中: a a a 是已知向量, S S S 是已知矩阵, w w w 是未知向量。 我们的目标是求解 w w w,即将 w w w 表示为 a a a 和 S S S 的函数。 情况 1:矩阵 S S S 可逆 如果矩…...
多线程事务管理:Spring Boot 实现全局事务回滚
多线程事务管理:Spring Boot 实现全局事务回滚 在日常开发中,我们常常会遇到需要在多线程环境下进行数据库操作的场景。这类操作的挑战在于如何保证多个线程中的数据库操作要么一起成功,要么一起失败,即 事务的原子性。尤其是在多个线程并发执行的情况下,确保事务的一致性…...
Vue3 中集成海康 H5 监控视频播放功能
🌈个人主页:前端青山 🔥系列专栏:Vue篇 🔖人终将被年少不可得之物困其一生 依旧青山,本期给大家带来Vuet篇专栏内容:Vue-集成海康 H5 监控视频播放功能 目录 一、引言 二、环境搭建 三、代码解析 子组件部分 1.…...
Linux: eBPF: libbpf-bootstrap-master 编译
文章目录 简介编译运行展示输出展示:简介 这个是使用libbpf的一个例子; 编译 如果是一个可以联网的机器,这个libbpf-bootstrap的编译就方便了,完全是自动化的下载依赖文件;如果没有,就只能自己准备这些个软件。 需要:libbpf-static; [root@RH8-LCP c]# makeLIB …...
1.1.4 计算机网络的分类
按分布范围分类: 广域网(wan) 城域网(man) 局域网(lan) 个域网(pan) 注意:如今局域网几乎采用“以太网技术实现”,因此“以太网”几乎成了“局域…...
周家庄智慧旅游小程序
项目概述 周家庄智慧旅游小程序将通过数字化手段提升游客的旅游体验,依托周家庄的自然与文化资源,打造智慧旅游新模式。该小程序将结合虚拟现实(VR)、增强现实(AR)和人工智能等技术,提供丰富的…...
【在Linux世界中追寻伟大的One Piece】命名管道
目录 1 -> 命名管道 1.1 -> 创建一个命名管道 1.2 -> 匿名管道与命名管道的区别 1.3 -> 命名管道的打开规则 1.4 -> 例子 1 -> 命名管道 管道应用的一个限制就是只能在具有共同祖先(具有亲缘关系)的进程间通信。如果我们想在不相关的进程之间交换数据&…...
如意控物联网项目-ML307R模组软件及硬件调试环境搭建
软件及硬件调试环境搭建 1、 软件环境搭建及编译 a) 打开官方SDK,内涵APP-DEMO,通过vscode打开程序, 软件程序编写及编译参考下边说明文档链接 OneMO线上服务平台 编译需预安装python3.7以上版本,安装完python后,打开…...
大模型分布式训练并行技术(九)-总结
近年来,随着Transformer、MOE架构的提出,使得深度学习模型轻松突破上万亿规模参数,传统的单机单卡模式已经无法满足超大模型进行训练的要求。因此,我们需要基于单机多卡、甚至是多机多卡进行分布式大模型的训练。 而利用AI集群&a…...
uniapp view设置当前view之外的点击事件
推荐学习文档 golang应用级os框架,欢迎stargolang应用级os框架使用案例,欢迎star案例:基于golang开发的一款超有个性的旅游计划app经历golang实战大纲golang优秀开发常用开源库汇总想学习更多golang知识,这里有免费的golang学习笔…...
【Mybatis篇】动态SQL的详细带练
🧸安清h:个人主页 🎥个人专栏:【计算机网络】 🚦作者简介:一个有趣爱睡觉的intp,期待和更多人分享自己所学知识的真诚大学生。 文章目录 🎯一.动态SQL简单介绍 🚦动态S…...
R语言AI模型部署方案:精准离线运行详解
R语言AI模型部署方案:精准离线运行详解 一、项目概述 本文将构建一个完整的R语言AI部署解决方案,实现鸢尾花分类模型的训练、保存、离线部署和预测功能。核心特点: 100%离线运行能力自包含环境依赖生产级错误处理跨平台兼容性模型版本管理# 文件结构说明 Iris_AI_Deployme…...
基于服务器使用 apt 安装、配置 Nginx
🧾 一、查看可安装的 Nginx 版本 首先,你可以运行以下命令查看可用版本: apt-cache madison nginx-core输出示例: nginx-core | 1.18.0-6ubuntu14.6 | http://archive.ubuntu.com/ubuntu focal-updates/main amd64 Packages ng…...
GitHub 趋势日报 (2025年06月08日)
📊 由 TrendForge 系统生成 | 🌐 https://trendforge.devlive.org/ 🌐 本日报中的项目描述已自动翻译为中文 📈 今日获星趋势图 今日获星趋势图 884 cognee 566 dify 414 HumanSystemOptimization 414 omni-tools 321 note-gen …...
Maven 概述、安装、配置、仓库、私服详解
目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...
python报错No module named ‘tensorflow.keras‘
是由于不同版本的tensorflow下的keras所在的路径不同,结合所安装的tensorflow的目录结构修改from语句即可。 原语句: from tensorflow.keras.layers import Conv1D, MaxPooling1D, LSTM, Dense 修改后: from tensorflow.python.keras.lay…...
AGain DB和倍数增益的关系
我在设置一款索尼CMOS芯片时,Again增益0db变化为6DB,画面的变化只有2倍DN的增益,比如10变为20。 这与dB和线性增益的关系以及传感器处理流程有关。以下是具体原因分析: 1. dB与线性增益的换算关系 6dB对应的理论线性增益应为&…...
【JVM面试篇】高频八股汇总——类加载和类加载器
目录 1. 讲一下类加载过程? 2. Java创建对象的过程? 3. 对象的生命周期? 4. 类加载器有哪些? 5. 双亲委派模型的作用(好处)? 6. 讲一下类的加载和双亲委派原则? 7. 双亲委派模…...
在鸿蒙HarmonyOS 5中使用DevEco Studio实现企业微信功能
1. 开发环境准备 安装DevEco Studio 3.1: 从华为开发者官网下载最新版DevEco Studio安装HarmonyOS 5.0 SDK 项目配置: // module.json5 {"module": {"requestPermissions": [{"name": "ohos.permis…...
鸿蒙(HarmonyOS5)实现跳一跳小游戏
下面我将介绍如何使用鸿蒙的ArkUI框架,实现一个简单的跳一跳小游戏。 1. 项目结构 src/main/ets/ ├── MainAbility │ ├── pages │ │ ├── Index.ets // 主页面 │ │ └── GamePage.ets // 游戏页面 │ └── model │ …...
使用SSE解决获取状态不一致问题
使用SSE解决获取状态不一致问题 1. 问题描述2. SSE介绍2.1 SSE 的工作原理2.2 SSE 的事件格式规范2.3 SSE与其他技术对比2.4 SSE 的优缺点 3. 实战代码 1. 问题描述 目前做的一个功能是上传多个文件,这个上传文件是整体功能的一部分,文件在上传的过程中…...
