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

jpa 修改信息拦截

实现目标springboot+JPA

哪个人,修改了哪个表的哪个字段,从什么值修改成什么值

import jakarta.persistence.*;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowire;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Configurable;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;@Component // 必须加
@Slf4j
@Configurable(autowire = Autowire.BY_TYPE)
public class AuditingEntityListener  {// 线程变量,保存修改前的 objectprivate ThreadLocal<Object> updateBeforeObject = new ThreadLocal<>();// 线程池static ThreadPoolExecutor executor = new ThreadPoolExecutor(Runtime.getRuntime().availableProcessors()+1, 30, 10,TimeUnit.SECONDS, new LinkedBlockingQueue(20),new ThreadPoolExecutor.CallerRunsPolicy());// EntityManager 操作数据库private static EntityManager entityManager;// requestprivate static HttpServletRequest request;@Autowiredpublic synchronized void setInfo(EntityManager entityManager,HttpServletRequest request) {AuditingEntityListener.entityManager = entityManager;AuditingEntityListener.request = request;}@PrePersistpublic void onCreateBefore(Object object) {// 在新实体持久化之前(即在数据库插入之前)调用System.out.println("在新实体持久化之前"+object);}@PostPersistpublic void onCreateAfter(Object object) {try {// object// 异步线程保存信息 入库executor.execute(()->{  });}catch (Exception e){}}@PreUpdatepublic void onUpdateBefore(Object object){System.out.println("在实体更新之前调用");// 用户名String userName = StringUtils.isBlank(request.getHeader("userName"))? "未知用户":request.getHeader("userName");System.out.println("修改人: " + userName);try {// 根据object对象获取主键名称,并根据主键获取对应的值Long id = SwaggerUtils.getIdFieldName(object);if(ObjectUtils.isEmpty(id)){return;}// 在新实体持久化之前(即在新数据插入之前)调用。// 根据ID获取该实体类库中的数据Future<Object> submit =  executor.submit(() -> entityManager.find(object.getClass(), id));// 阻塞主线程 [1s 超时],等待异步线程返回数据,将内容加入到线程变量if(!ObjectUtils.isEmpty(submit.get(1,TimeUnit.SECONDS))){updateBeforeObject.set(submit.get());}}catch (Exception e){log.error("异步信息获取失败={},",e.toString());}}@PostUpdatepublic void onUpdateAfter(Object object) {try {// 在实体更新之后调用。System.out.println("在实体更新之后调用");// 获取字段名,字段值,字段类型// getFields(object);// 获取修改后的字段区别//   有 swagger依赖,且 对应的实体有 @ApiModelProperty,则取 注释名,否则取真实字段名, 例//   @ApiModelProperty(value = "姓名")//    private String name;////    @Column(length = 200)//    private String addr;////    改动字段 [姓名]: [ 阿达 ] -> [ 77 ]//    改动字段 [addr]: [ 阿达 ] -> [ 77 ]if(!ObjectUtils.isEmpty(updateBeforeObject.get())){List<String> objectDifferetList = objectDifferet(updateBeforeObject.get(),object);objectDifferetList.forEach(e->{System.err.println(e);});// 移除此次操作updateBeforeObject.remove();// 异步线程保存 改动信息 入库executor.execute(()->{  });}}catch (Exception e){log.error("异步信息保存失败");}}@PreRemovepublic void onRemoveBefore(Object object) {// 在删除实体之前调用。System.out.println("在删除实体之前调用"+object);}@PostRemovepublic void onRemoveAfter(Object object) {// 在删除实体之后调用System.out.println("在删除实体之后调用"+object);}// 获取实体 字段 和 值public static void getFields(Object object){Class<?> clazz = object.getClass();Field[] fields = clazz.getDeclaredFields();StringBuilder stringBuilder = new StringBuilder();// 遍历所有字段for (Field field : fields) {// 确保私有字段也可以被访问field.setAccessible(true);try {// 获取字段的名称String fieldName = field.getName();// 获取字段的值Object fieldValue = field.get(object);// 获取字段的类型Class<?> fieldType = field.getType();// 打印字段的名称和类型System.out.println("字段名: " + fieldName + ", 字段值:"+ fieldValue + ", 字段类型: " + fieldType.getName());}catch (Exception e){System.out.println("field:获取失败={}"+field);}}}// 获取两个实体类字段之间的区别public static List<String> objectDifferet(Object obj1, Object obj2) {System.err.println("原始object:" + obj1);System.err.println("==================");System.err.println("新的object:" + obj2);List<String> differences = new ArrayList<>();if (obj1 == null || obj2 == null) {throw new IllegalArgumentException("Both objects must be non-null");}if (!obj1.getClass().equals(obj2.getClass())) {throw new IllegalArgumentException("Objects must be of the same type");}Class<?> clazz = obj1.getClass();Field[] fields = clazz.getDeclaredFields();for (Field field : fields) {field.setAccessible(true); // Ensure private fields are accessibletry {Object value1 = field.get(obj1);Object value2 = field.get(obj2);if ((value1 != null && !value1.equals(value2)) || (value1 == null && value2 != null)) {String key = ObjectUtils.isEmpty( SwaggerUtils.getApiModelProperty(clazz,field.getName()) ) ? field.getName() : SwaggerUtils.getApiModelProperty(clazz,field.getName()).value() ;String table = ObjectUtils.isEmpty( SwaggerUtils.getTable(clazz) ) ? clazz.getName()+"实体" : SwaggerUtils.getTable(clazz).name() ;differences.add(String.format("表名 %s 字段  %s("+field.getName()+")  :  由 [ %s ] 改为 [ %s ]",table, key , value1, value2));}} catch (IllegalAccessException e) {e.printStackTrace(); // Handle exception as appropriate for your use case}}return differences;}}
import io.swagger.annotations.ApiModelProperty;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import java.lang.reflect.Field;public class SwaggerUtils {// 获取 标注了ApiModelProperty 对应的注释public static ApiModelProperty getApiModelProperty(Class<?> clazz, String fieldName) {try {Field field = clazz.getDeclaredField(fieldName);return field.getAnnotation(ApiModelProperty.class);} catch (NoSuchFieldException e) {e.printStackTrace();}return null;}// 获取标注了@Table 的表名public static Table getTable(Class<?> clazz) {try {return clazz.getAnnotation(Table.class);} catch (Exception e) {e.printStackTrace();}return null;}/*** 根据 传来的实体 获取主键id 对应的值!*/public static Long getIdFieldName(Object obj)  {try {Class<?> clazz = obj.getClass();Field[] fields = clazz.getDeclaredFields();for (Field field : fields) {if (field.isAnnotationPresent(Id.class)) {field.setAccessible(true);Object idValue = field.get(obj);Long id = (Long) idValue;return id;}}} catch (Exception e) {e.printStackTrace();}return null;}}
实体@Entity//实体
@Data
@Accessors(chain = true)
@AllArgsConstructor
@NoArgsConstructor
@Table(name = "user_abc")
@EntityListeners({AuditingEntityListener.class})
public class User  implements Serializable {@Id //主键@GeneratedValue(strategy = GenerationType.IDENTITY) //主键id生成策略,IDENTITY:自增private Long id;@Column(nullable = false,length = 200)// 非空 唯一 200长度@ApiModelProperty(value = "姓名")private String name;@Column(length = 200)private String addr;@Column(length = 200)private String phone;@Column(length = 200)
//    @Transientprivate String haha;}

修改接口
在这里插入图片描述

在这里插入图片描述

user_abc表

在这里插入图片描述

最终效果

在这里插入图片描述
在这里插入图片描述

相关文章:

jpa 修改信息拦截

实现目标springbootJPA 哪个人&#xff0c;修改了哪个表的哪个字段&#xff0c;从什么值修改成什么值 import jakarta.persistence.*; import jakarta.servlet.http.HttpServletRequest; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; im…...

JavaEE 09 锁策略

1.锁策略 1.1 乐观锁与悲观锁 其实前三个锁是同一种锁,只是站在不同的角度上去进行描述,此处的乐观与悲观其实是指在预测的角度上看会发生锁竞争的概率大小,概率大的则是悲观锁,概率小的则是乐观锁 乐观锁在加锁的时候就会做较少的事情,加锁的速度较快,但是消耗的cpu资源等也会…...

javacv的视频截图功能

之前做了一个资源库的小项目&#xff0c;因为上传资源文件包含视频等附件&#xff0c;所以就需要时用到这个功能。通过对视频截图&#xff0c;然后作为封面缩略图&#xff0c;达到美观效果。 首先呢&#xff0c;需要准备相关的jar包&#xff0c;之前我用的是低版本的1.4.2&…...

Fiddler中AutoResponder的简单使用

AutoResponder&#xff0c;自动回复器&#xff0c;用于将 HTTP 请求重定向为指定的返回类型。 这个功能有点像是一个代理转发器&#xff0c;可以将某一请求的响应结果替换成指定的资源&#xff0c;可以是某个页面也可以是某个本地文件 1.使用 打开“Fiddler”&#xff0c;点击…...

K8S(一)—安装部署

目录 安装部署前提以下的操作指导(在master)之前都是三台机器都需要执行 安装docker服务下面的操作仅在k8smaster执行 安装部署 前提 以下的操作指导(在master)之前都是三台机器都需要执行 关闭防火墙 [rootk8smaster ~]# vim /etc/selinux/config [rootk8smaster ~]# swa…...

Kubernetes Pod 网段与主机内网网段互通

开发环境的需求 开发环境部署 K8s 后&#xff0c;服务器会部署在 K8s 里&#xff0c;通常 Pod 网段被隔离&#xff0c;主机无法访问 实际开发需求&#xff0c;往往需要当前开发调试的服务主机本地部署&#xff0c;其他服则在 K8s 内 因此&#xff0c;使用 K8s &#xff0c;必…...

go学习redis的学习与使用

文章目录 一、redis的学习与使用1.Redis的基本介绍2.Redis的安装下载安装包即可3.Redis的基本使用1&#xff09;Redis的启动&#xff1a;2&#xff09;Redis的操作的三种方式3&#xff09;说明&#xff1a;Redis安装好后&#xff0c;默认有16个数据库&#xff0c;初始默认使用0…...

娱乐新拐点:TikTok如何改变我们的日常生活?

在数字时代的浪潮中&#xff0c;社交媒体平台不断涌现&#xff0c;其中TikTok以其独特的短视频内容在全球范围内掀起了一场娱乐革命。本文将深入探讨TikTok如何改变我们的日常生活&#xff0c;从社交互动、文化传播到个人创意表达&#xff0c;逐步改写了娱乐的新篇章。 短视频潮…...

【Nginx】Nginx了解(基础)

文章目录 Nginx产生的原因Nginx简介Nginx的作用反向代理负载均衡策略动静分离 Nginx的Windows下的安装Linux下的安装Nginx常用命令 负载均衡功能演示 Nginx产生的原因 背景 一个公司的项目刚刚上线的时候&#xff0c;并发量小&#xff0c;用户使用的少&#xff0c;所以在低并发…...

十九)Stable Diffusion使用教程:ai室内设计案例

今天我们聊聊如何通过SD进行室内设计装修。 方式一:controlnet的seg模型 基础起手式: 选择常用算法,抽卡: 抽到喜欢的图片之后,拖到controlnet里: 选择seg的ade20k预处理器,点击爆炸按钮,得到seg语义分割图,下载下来: 根据语义分割表里的颜色值,到PS里进行修改: 语…...

虚拟机VMware安装centos以及配置网络

目录 1、CentOS7的下载2、CentOS7的配置3、CentOS7的安装4、CentOS7的网络配置 4.1、自动获取IP4.2、固定获取IP 5、XShell连接CentO 准备工作&#xff1a;提前下载和安装好VMware。VMware的安装可以参考这一篇文章&#xff1a;VMware15的下载及安装教程。 1、CentOS7的下载 …...

call 和 apply:改变对象行为的秘密武器(上)

&#x1f90d; 前端开发工程师&#xff08;主业&#xff09;、技术博主&#xff08;副业&#xff09;、已过CET6 &#x1f368; 阿珊和她的猫_CSDN个人主页 &#x1f560; 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 &#x1f35a; 蓝桥云课签约作者、已在蓝桥云…...

工作中 docker 的使用积累

2 进入 openwrt 容器 docker exec -it openwrt /bin/sh3 查看 docker 信息 docker info4 启动容器 4 挂载 overlay mount -t overlay overlay -o lowerdirA:B,upperdirC,workdirworker /tmp/test -t overlay &#xff1a; 指定要挂载的文件系统类型为 overlayoverlay: 指定…...

初识SpringSecurity

目录 前言 特点 快速开始 导入依赖 运行项目 访问服务 权限控制 实现UserDetails接口 添加SecurityConfig配置类 测试接口DemoController 设置权限控制authorizeHttpRequests 结果分析 总结 前言 Spring Security是一个强大且高度可定制的身份验证和访问控制框架…...

大数据讲课笔记1.4 进程管理

文章目录 零、学习目标一、导入新课二、新课讲解&#xff08;一&#xff09;进程概述1、基本概念2、三维度看待进程3、引入多道编程模型&#xff08;1&#xff09;CPU利用率与进程数关系&#xff08;2&#xff09;从三个视角看多进程 4、进程的产生和消亡&#xff08;1&#xf…...

技术点:实现大文件上传

大文件上传 实现思路 对于大文件上传考虑到上传时间太久、超出浏览器响应时间、提高上传效率、优化上传用户体验等问题进行了深入探讨&#xff0c;以下初略罗列各个知识点的实现思路&#xff1a; 大文件上传对文件本身进行了文件流内容 Blob 的分割&#xff0c;使用 Blob.pr…...

记一次挖矿病毒的溯源

ps&#xff1a;因为项目保密的原因部分的截图是自己在本地的环境复现。 1. 起因 客户打电话过来说&#xff0c;公司web服务异常卡顿。起初以为是web服务缓存过多导致&#xff0c;重启几次无果后觉得可能是受到了攻击。起初以为是ddos攻击&#xff0c;然后去查看web服务器管理…...

day05-报表技术-图形报表

1、图表报表简介 ​ 在大数据时代&#xff0c;人们需要对大量的数据进行分析&#xff0c;帮助用户或公司领导更直观的察觉差异&#xff0c;做出判断&#xff0c;减少时间成本&#xff0c;而在web项目中除了表格显示数据外&#xff0c;还可以通过图表来表现数据&#xff0c;这种…...

【Spring】@Transactional事务属性详解

文章目录 1、事务传播行为注意事务传播行为在不同类之间调用生效Propagation.REQUIRED(默认传播行为)Propagation.REQUIRES_NEWPropagation.NESTED 2、事务的隔离级别隔离级别设置 3、设置事务异常回滚3.1、默认情况3.2、设置回滚异常3.3、设置不回滚的异常 4、超时时间5、只读…...

通过css3的锚定滚动属性,实现分页加载时让滚动条不闪动

html标签 <div scroll"handleScroll" id"list-container"style"overflow-anchor:auto;overflow-y: auto;height: 80vh"><ul id"talks"v-for"(item,index) in msgList":key"item.roleiditem.timeitem.conten…...

学校招生小程序源码介绍

基于ThinkPHPFastAdminUniApp开发的学校招生小程序源码&#xff0c;专为学校招生场景量身打造&#xff0c;功能实用且操作便捷。 从技术架构来看&#xff0c;ThinkPHP提供稳定可靠的后台服务&#xff0c;FastAdmin加速开发流程&#xff0c;UniApp则保障小程序在多端有良好的兼…...

最新SpringBoot+SpringCloud+Nacos微服务框架分享

文章目录 前言一、服务规划二、架构核心1.cloud的pom2.gateway的异常handler3.gateway的filter4、admin的pom5、admin的登录核心 三、code-helper分享总结 前言 最近有个活蛮赶的&#xff0c;根据Excel列的需求预估的工时直接打骨折&#xff0c;不要问我为什么&#xff0c;主要…...

鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个医院查看报告小程序

一、开发环境准备 ​​工具安装​​&#xff1a; 下载安装DevEco Studio 4.0&#xff08;支持HarmonyOS 5&#xff09;配置HarmonyOS SDK 5.0确保Node.js版本≥14 ​​项目初始化​​&#xff1a; ohpm init harmony/hospital-report-app 二、核心功能模块实现 1. 报告列表…...

Mac软件卸载指南,简单易懂!

刚和Adobe分手&#xff0c;它却总在Library里给你写"回忆录"&#xff1f;卸载的Final Cut Pro像电子幽灵般阴魂不散&#xff1f;总是会有残留文件&#xff0c;别慌&#xff01;这份Mac软件卸载指南&#xff0c;将用最硬核的方式教你"数字分手术"&#xff0…...

OpenLayers 分屏对比(地图联动)

注&#xff1a;当前使用的是 ol 5.3.0 版本&#xff0c;天地图使用的key请到天地图官网申请&#xff0c;并替换为自己的key 地图分屏对比在WebGIS开发中是很常见的功能&#xff0c;和卷帘图层不一样的是&#xff0c;分屏对比是在各个地图中添加相同或者不同的图层进行对比查看。…...

【数据分析】R版IntelliGenes用于生物标志物发现的可解释机器学习

禁止商业或二改转载&#xff0c;仅供自学使用&#xff0c;侵权必究&#xff0c;如需截取部分内容请后台联系作者! 文章目录 介绍流程步骤1. 输入数据2. 特征选择3. 模型训练4. I-Genes 评分计算5. 输出结果 IntelliGenesR 安装包1. 特征选择2. 模型训练和评估3. I-Genes 评分计…...

AirSim/Cosys-AirSim 游戏开发(四)外部固定位置监控相机

这个博客介绍了如何通过 settings.json 文件添加一个无人机外的 固定位置监控相机&#xff0c;因为在使用过程中发现 Airsim 对外部监控相机的描述模糊&#xff0c;而 Cosys-Airsim 在官方文档中没有提供外部监控相机设置&#xff0c;最后在源码示例中找到了&#xff0c;所以感…...

【Redis】笔记|第8节|大厂高并发缓存架构实战与优化

缓存架构 代码结构 代码详情 功能点&#xff1a; 多级缓存&#xff0c;先查本地缓存&#xff0c;再查Redis&#xff0c;最后才查数据库热点数据重建逻辑使用分布式锁&#xff0c;二次查询更新缓存采用读写锁提升性能采用Redis的发布订阅机制通知所有实例更新本地缓存适用读多…...

STM32HAL库USART源代码解析及应用

STM32HAL库USART源代码解析 前言STM32CubeIDE配置串口USART和UART的选择使用模式参数设置GPIO配置DMA配置中断配置硬件流控制使能生成代码解析和使用方法串口初始化__UART_HandleTypeDef结构体浅析HAL库代码实际使用方法使用轮询方式发送使用轮询方式接收使用中断方式发送使用中…...

c++第七天 继承与派生2

这一篇文章主要内容是 派生类构造函数与析构函数 在派生类中重写基类成员 以及多继承 第一部分&#xff1a;派生类构造函数与析构函数 当创建一个派生类对象时&#xff0c;基类成员是如何初始化的&#xff1f; 1.当派生类对象创建的时候&#xff0c;基类成员的初始化顺序 …...