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

在这里插入图片描述

最终效果

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

相关文章:

  • 信息学奥赛一本通 第二章 顺序结构程序设计 第一、二节C语言非C++
  • 【网络编程】-- 04 UDP
  • Windows mysql5.7 执行查询/开启/测试binlog---简易记录
  • 阿木实验室普罗米修斯项目环境配置
  • Centos7部署SVN
  • 基于CNN+数据增强+残差网络Resnet50的少样本高准确度猫咪种类识别—深度学习算法应用(含全部工程源码)+数据集+模型(六)
  • 通过css3的锚定滚动属性,实现分页加载时让滚动条不闪动
  • 【Spring Boot】快速入门
  • Modbus转Profinet网关使用方法
  • 100天精通风控建模(原理+Python实现)——第10天:风控建模中主成分析是什么?怎么实现?
  • 2023-12-14 二叉树的最大深度和二叉树的最小深度以及完全二叉树的节点个数
  • uniapp组件map地图组件使用
  • Linux(操作系统)面经——part2
  • 如何安装LUT预设?达芬奇/FCP/PR怎么安装LUT预设.cube格式文件的教程
  • 【产品经理】产品增效项目落地,项目反哺产品成长
  • (ckeditor+ckfinder用法)Jquery,js获取ckeditor值
  • [js高手之路]搞清楚面向对象,必须要理解对象在创建过程中的内存表示
  • 2017-08-04 前端日报
  • javascript 哈希表
  • Mysql5.6主从复制
  • Three.js 再探 - 写一个跳一跳极简版游戏
  • vue从入门到进阶:计算属性computed与侦听器watch(三)
  • 关于extract.autodesk.io的一些说明
  • 互联网大裁员:Java程序员失工作,焉知不能进ali?
  • 技术发展面试
  • 设计模式 开闭原则
  • 手机app有了短信验证码还有没必要有图片验证码?
  • ​马来语翻译中文去哪比较好?
  • #mysql 8.0 踩坑日记
  • #前后端分离# 头条发布系统
  • (20050108)又读《平凡的世界》
  • (Arcgis)Python编程批量将HDF5文件转换为TIFF格式并应用地理转换和投影信息
  • (c语言)strcpy函数用法
  • (poj1.3.2)1791(构造法模拟)
  • (solr系列:一)使用tomcat部署solr服务
  • (附源码)基于SSM多源异构数据关联技术构建智能校园-计算机毕设 64366
  • (十七)Flask之大型项目目录结构示例【二扣蓝图】
  • (一)Thymeleaf用法——Thymeleaf简介
  • ****Linux下Mysql的安装和配置
  • .gitignore文件设置了忽略但不生效
  • .NET Framework Client Profile - a Subset of the .NET Framework Redistribution
  • .NET 设计模式—适配器模式(Adapter Pattern)
  • .Net 中Partitioner static与dynamic的性能对比
  • .NET/C# 使用 #if 和 Conditional 特性来按条件编译代码的不同原理和适用场景
  • .net6使用Sejil可视化日志
  • .NET程序员迈向卓越的必由之路
  • .net开发引用程序集提示没有强名称的解决办法
  • .NET正则基础之——正则委托
  • .Net转Java自学之路—基础巩固篇十三(集合)
  • @JsonSerialize注解的使用
  • @Tag和@Operation标签失效问题。SpringDoc 2.2.0(OpenApi 3)和Spring Boot 3.1.1集成
  • []C/C++读取串口接收到的数据程序
  • [AR Foundation] 人脸检测的流程
  • [C/C++] -- 二叉树
  • [CSAWQual 2019]Web_Unagi ---不会编程的崽