当前位置: 首页 > 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…...

[特殊字符] 智能合约中的数据是如何在区块链中保持一致的?

&#x1f9e0; 智能合约中的数据是如何在区块链中保持一致的&#xff1f; 为什么所有区块链节点都能得出相同结果&#xff1f;合约调用这么复杂&#xff0c;状态真能保持一致吗&#xff1f;本篇带你从底层视角理解“状态一致性”的真相。 一、智能合约的数据存储在哪里&#xf…...

多云管理“拦路虎”:深入解析网络互联、身份同步与成本可视化的技术复杂度​

一、引言&#xff1a;多云环境的技术复杂性本质​​ 企业采用多云策略已从技术选型升维至生存刚需。当业务系统分散部署在多个云平台时&#xff0c;​​基础设施的技术债呈现指数级积累​​。网络连接、身份认证、成本管理这三大核心挑战相互嵌套&#xff1a;跨云网络构建数据…...

论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(一)

宇树机器人多姿态起立控制强化学习框架论文解析 论文解读&#xff1a;交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架&#xff08;一&#xff09; 论文解读&#xff1a;交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化…...

【RockeMQ】第2节|RocketMQ快速实战以及核⼼概念详解(二)

升级Dledger高可用集群 一、主从架构的不足与Dledger的定位 主从架构缺陷 数据备份依赖Slave节点&#xff0c;但无自动故障转移能力&#xff0c;Master宕机后需人工切换&#xff0c;期间消息可能无法读取。Slave仅存储数据&#xff0c;无法主动升级为Master响应请求&#xff…...

算法:模拟

1.替换所有的问号 1576. 替换所有的问号 - 力扣&#xff08;LeetCode&#xff09; ​遍历字符串​&#xff1a;通过外层循环逐一检查每个字符。​遇到 ? 时处理​&#xff1a; 内层循环遍历小写字母&#xff08;a 到 z&#xff09;。对每个字母检查是否满足&#xff1a; ​与…...

Go 语言并发编程基础:无缓冲与有缓冲通道

在上一章节中&#xff0c;我们了解了 Channel 的基本用法。本章将重点分析 Go 中通道的两种类型 —— 无缓冲通道与有缓冲通道&#xff0c;它们在并发编程中各具特点和应用场景。 一、通道的基本分类 类型定义形式特点无缓冲通道make(chan T)发送和接收都必须准备好&#xff0…...

MySQL 知识小结(一)

一、my.cnf配置详解 我们知道安装MySQL有两种方式来安装咱们的MySQL数据库&#xff0c;分别是二进制安装编译数据库或者使用三方yum来进行安装,第三方yum的安装相对于二进制压缩包的安装更快捷&#xff0c;但是文件存放起来数据比较冗余&#xff0c;用二进制能够更好管理咱们M…...

C++课设:简易日历程序(支持传统节假日 + 二十四节气 + 个人纪念日管理)

名人说:路漫漫其修远兮,吾将上下而求索。—— 屈原《离骚》 创作者:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊) 专栏介绍:《编程项目实战》 目录 一、为什么要开发一个日历程序?1. 深入理解时间算法2. 练习面向对象设计3. 学习数据结构应用二、核心算法深度解析…...

站群服务器的应用场景都有哪些?

站群服务器主要是为了多个网站的托管和管理所设计的&#xff0c;可以通过集中管理和高效资源的分配&#xff0c;来支持多个独立的网站同时运行&#xff0c;让每一个网站都可以分配到独立的IP地址&#xff0c;避免出现IP关联的风险&#xff0c;用户还可以通过控制面板进行管理功…...

关于uniapp展示PDF的解决方案

在 UniApp 的 H5 环境中使用 pdf-vue3 组件可以实现完整的 PDF 预览功能。以下是详细实现步骤和注意事项&#xff1a; 一、安装依赖 安装 pdf-vue3 和 PDF.js 核心库&#xff1a; npm install pdf-vue3 pdfjs-dist二、基本使用示例 <template><view class"con…...