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

Spring Data Envers 数据审计实战2 - 自定义监听程序扩展审计字段及字段值

上篇讲述了如何在Spring项目中集成Spring Data Envers做数据审计和历史版本查看功能。

之前演示的是业务表中已有的字段进行审计,那么如果我们想扩展审计字段呢?

比如目前对员工表加入了@Audited审计,员工表有个字段为dept_id,为了页面展示更人性化,我想把dept_id关联的部门名称(当时的快照值)也存入审计版本中,这样的话,在查看员工信息修改历史的时候,就可以看到当时员工对应的部门名称了。

我们看看如何把引用的快照信息保存到审计版本记录中,经过对spring data envers (基于hibernate envers)的源码阅读,发现目前没有可用手段能将额外的自定义审计信息保存到业务表对应的_aud审计表_aud表保存的字段比较固定,就是业务表中被审计的字段 + 审计基础字段(rev,revtype, revend, revend_tstmp, 分别为:审计版本号、操作类型【增删改】、审计下一个版本号、操作时间戳)。

那么就只能寄希望于审计实体表(revinfo)了,还好hibernate envers给我们留了口子去扩展这个表。

1. 自定义审计实体类,表名可以用revinfo也可以用其他的,必须要有审计版本/id和操作时间戳两个字段,可以自己加入新的字段(如下列代码中的extInfo字段)。

@Entity(name = "revinfo") //审计实体表名
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@RevisionEntity(MyRevisionListener.class) //自定义的监听(可处理扩展字段保存值)
public class MyRevisionEntity {private static final long serialVersionUID = 1L;@Id@GeneratedValue@RevisionNumber@Column(name = "rev")private int revNumb; //审计版本号 必要@RevisionTimestamp@Column(name = "revtstmp")private long timestamp; //审计时间 必要@Column(name = "ext", length = 1000)private String extInfo; //扩展字段
}

2. 我们看到上面还指定了一个监听类,没错,我们接下来就要创建这个类,并在这个类中定义当保存审计信息时我们要在扩展字段去插入什么值。

网上很多是实现RevisionListener接口,这里我建议实现EntityTrackingRevisionListener接口,它比RevisionListener更强大,多了entityChanged方法,可以帮助我们处理更多自定义的业务,且不污染业务审计表。

@Slf4j
public class MyRevisionListener implements EntityTrackingRevisionListener {@Overridepublic void newRevision(Object revisionEntity) {//这里可以自行设置业务表中被审计的字段保存何值到DB _aud表,字段类型需要与业务表中定义的一致,一般为了保持数据一致性,这里不做处理}@Overridepublic void entityChanged(Class entityClass, String entityName, Object entityId, RevisionType revisionType, Object revisionEntity) {//我们扩展额外的字段及字段值主要是重写这个方法......}
}

3. 那么我们如何控制在save员工信息的时候,将员工部门名称快照保存到审计实体表(revinfo)呢?

首先,你需要通过一些辅助方式将员工表和部门表关联起来,如果你们配置了@ManyToOne这类注解的话,可以读取注解进行操作。如果没有的话,建议自定义注解,简单配置关联关系即可。

我这里自定义了一个注解@AuditedExtInfo,可以配置被审计的字段需要保存什么值到revinfo表:

@AuditedExtInfo(referenceClass = Dept.class, displayField = "name") //自定义注解配置dept_id对应的是哪个实体类、以及保存哪个字段的快照值
@Column(name = "dept_id", length = 50)
private String deptId;

然后,在监听程序中,我们通过反射获取到deptId关联的部门对象的名称,进行保存:

    @SneakyThrows@Overridepublic void entityChanged(Class entityClass, String entityName, Object entityId, RevisionType revisionType, Object revisionEntity) {//将审计实体对象转换为自定义的审计实体类的对象MyRevisionEntityMyRevisionEntity myRevisionEntity = (MyRevisionEntity) revisionEntity;//获取Spring Data JPA的Repository (方便获取实体对象及关联的对象信息)//ApplicationContextHelper.getContext()这个是返回Spring的ApplicationContext的静态方法Repositories repositories = new Repositories(ApplicationContextHelper.getContext());//先获取到被审计的业务实体表对象,例如:我们需要找到此次save操作对应的员工信息,从而找到deptIdObject entityRepository = repositories.getRepositoryFor(entityClass).orElseThrow(() -> new RuntimeException("Not found entityRepository"));Object entity = ((Optional) MethodUtils.invokeMethod(entityRepository, "findById", entityId)).orElse(null);//遍历业务实体表的字段列表,找到注解字段信息Field[] fields = entityClass.getDeclaredFields();for (Field field : fields) {AuditedExtInfo conversion = field.getDeclaredAnnotation(AuditedExtInfo.class);if (Objects.isNull(conversion)) {//如果没有定义@AuditedExtInfo注解,则忽略此字段continue;}//找到了注解,获取该字段的值,然后用该字段的值(deptId)去查部门表的Repository的findById方法,找到部门对象String fieldValue = String.valueOf(FieldUtils.readDeclaredField(entity, field.getName(), true));Object referenceRepository = repositories.getRepositoryFor(conversion.referenceClass()).orElseThrow(() -> new RuntimeException("Not found referenceRepository"));Object referenceObj = ((Optional) MethodUtils.invokeMethod(referenceRepository, "findById", fieldValue)).orElse(null);//获取引用对象的相应字段的值,进行保存。如部门对象的name值String extInfo = String.valueOf(FieldUtils.readDeclaredField(referenceObj, conversion.displayField(), true));//如果此业务实体表要扩展多个字段的引用快照值,建议在myRevisionEntity的ext字段保存一个JSON或Map,或者定义多个字段 如ext1、ext2、ext3...myRevisionEntity.setExt(extInfo);break;}}

借助Hibernate envers预留的监听接口扩展自己的监听类,以及反射操作实体对象和JPA等手段,可以在Spring Data JPA进行save操作时,保存我们额外的审计字段及字段值。

相关文章:

Spring Data Envers 数据审计实战2 - 自定义监听程序扩展审计字段及字段值

上篇讲述了如何在Spring项目中集成Spring Data Envers做数据审计和历史版本查看功能。 之前演示的是业务表中已有的字段进行审计,那么如果我们想扩展审计字段呢? 比如目前对员工表加入了Audited审计,员工表有个字段为dept_id,为…...

一个 SpringBoot 项目能同时处理多少请求?

目录 1 问题分析 2 Demo 3 答案 4 怎么来的? 5 标准答案及影响参数一Tomcat配置 6 影响参数二 Web容器 7 影响参数三 Async 1 问题分析 一个 SpringBoot 项目能同时处理多少请求? 不知道你听到这个问题之后的第一反应是什么? 我大概…...

计算机网络——网络

计算机网络——网络 小程一言专栏链接: [link](http://t.csdnimg.cn/ZUTXU)前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家, [跳转到网站](https://www.captainbed.cn/qianqiu) 无线网络和移动网…...

C语言探索:选择排序的实现与解读

当我们需要对一组数据进行排序时,选择排序(Selection Sort)是一种简单但效率较低的排序算法。它的基本思想是每次从未排序的数据中选择最小(或最大)的元素,然后将其放置在已排序序列的末尾。通过重复这个过…...

Golang 学习(二)进阶使用

二、进阶使用 性能提升——协程 GoRoutine go f();一个 Go 线程上,可以起多个协程(有独立的栈空间、共享程序堆空间、调度由用户控制)主线程是一个物理线程,直接作用在 cpu 上的。是重量级的,非常耗费 cpu 资源。协…...

ubuntu22.04@laptop OpenCV定制化安装

ubuntu22.04laptop OpenCV定制化安装 1. 源由2. 默认配置3. 定制配置4. 定制安装5. 定制OpenCV-4.9.05.1 修改opencv.conf5.2 加载so文件5.3 修改bash环境变量5.4 增加pkgconfig5.5 检查OpenCV-4.9.0安装 6. 总结7. 参考资料 1. 源由 目前,能Google到的代码层次不齐…...

linux系统非关系型数据库redis

redis 介绍redis的特点:缓存 安装安装单机版redisredis的相关工具 介绍 redis是一个开源的、使用C语言编写的、支持网络交互的、可基于内存也可持久化的Key-Value数据库 redis的官网:redis.ioredis的特点: 丰富的数据结构 支持持久化 支持事务 支持主从缓存 类型 …...

【LeetCode: 292. Nim 游戏+ 博弈问题】

🚀 算法题 🚀 🌲 算法刷题专栏 | 面试必备算法 | 面试高频算法 🍀 🌲 越难的东西,越要努力坚持,因为它具有很高的价值,算法就是这样✨ 🌲 作者简介:硕风和炜,…...

Android 9.0 禁用adb reboot recovery命令实现正常重启功能

1.前言 在9.0的系统rom定制化开发中,在定制recovery模块的时候,由于产品开发需要要求禁用recovery的相关功能,比如在通过adb命令的 adb reboot recovery的方式进入recovery也需要实现禁用,所以就需要了解相关进入recovery流程来禁用该功能 2.禁用adb reboot recovery命…...

分析网站架构:浏览器插件

一、Wappalyzer 1.1 介绍 Wappalyzer 是一款用于识别网站所使用技术栈的浏览器插件。它能够分析正在浏览的网页,检测出网站所使用的各种技术和框架,如内容管理系统(CMS)、JavaScript库、Web服务器等。用户只需安装 Wappalyzer 插…...

CentOS7搭建Hadoop集群

准备工作 1、准备三台虚拟机,参考:CentOS7集群环境搭建(3台)-CSDN博客 2、配置虚拟机之间免密登录,参考:CentOS7集群配置免密登录-CSDN博客 3、虚拟机分别安装jdk,参考:CentOS7集…...

10.0 Zookeeper 权限控制 ACL

zookeeper 的 ACL(Access Control List,访问控制表)权限在生产环境是特别重要的,所以本章节特别介绍一下。 ACL 权限可以针对节点设置相关读写等权限,保障数据安全性。 permissions 可以指定不同的权限范围及角色。 …...

容器化技术基础概念:雪花服务器与凤凰服务器

雪花服务器与凤凰服务器:两种软件部署领域的基础设施对比 在软件部署领域,服务器管理在正常运行时间、效率和安全性方面发挥着关键作用。存在两种截然不同的方法:雪花服务器和凤凰服务器。了解它们之间的区别将帮助您选择最适合您需求的策略…...

解决maven 在IDEA 下载依赖包速度慢的问题

1.idea界面双击shift键 2.打开setting.xml文件 复制粘贴 <?xml version"1.0" encoding"UTF-8"?> <settings xmlns"http://maven.apache.org/SETTINGS/1.0.0"xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:sc…...

用python编写爬虫,爬取二手车信息+实验报告

题目 报告要求 工程报告链接放在这里 https://download.csdn.net/download/Samature/88805518使用 1.安装jupyter notebook 2.用jupyter notebook打开工程里的ipynb文件&#xff0c;再run all就行 注意事项 可能遇到的bug 暂无&#xff0c;有的话私信我...

代码随想录算法训练营第二十九天|491.非递减子序列、46.全排列、47.全排列II

491.非递减子序列 思路&#xff1a;这道题最开始的时候&#xff0c;我想到两个问题&#xff1a;一个是如何维持递增的序列&#xff0c;一个是如何去重&#xff0c;写了一版代码&#xff0c;用的前面的去重方法&#xff0c;但是遇到一个case始终过不了&#xff0c;[1,2,3,4,5,6,…...

(2)(2.14) SPL Satellite Telemetry

文章目录 前言 1 本地 Wi-Fi&#xff08;费用&#xff1a;30 美元以上&#xff0c;范围&#xff1a;室内&#xff09; 2 蜂窝电话&#xff08;费用&#xff1a;100 美元以上&#xff0c;范围&#xff1a;蜂窝电话覆盖区域&#xff09; 3 手机卫星&#xff08;费用&#xff…...

OTG -- STM32 OTG驱动代码下载及简述(三)

目录 前沿 1 STM32 OTG标准库的获取 2 设备模式代码匹配开发板 2.1 OTG FS全速代码修改 2.2 OTG HS代码修改 2.2.1 OTG HS外部高速PHY运行在高速模式代码修改 2.2.2 OTG HS外部高速PHY运行在全速模式代码修改 2.2.3 OTG HS内部全速PHY运行在全速模式代码修改 前沿 前面…...

STM32F407 CAN参数配置 500Kbps

本篇CAN参数适用 芯片型号&#xff1a;STM32F407xx系统时钟&#xff1a;168MHz&#xff0c;CAN挂载总线APB1为42M波 特 率 &#xff1a;500Kpbs引脚使用&#xff1a;TX_PB9&#xff0c;RX_PB8&#xff1b;修改为PA11PA12后&#xff0c;参数不变。 步骤一、打勾开启CAN&#xf…...

python常用的深度学习框架

目录 一&#xff1a;介绍 二&#xff1a;使用 Python中有几个非常受欢迎的深度学习框架&#xff0c;它们提供了构建和训练神经网络所需的各种工具和库。以下是一些最常用的Python深度学习框架&#xff1a; 一&#xff1a;介绍 TensorFlow&#xff1a;由Google开发的TensorF…...

第19节 Node.js Express 框架

Express 是一个为Node.js设计的web开发框架&#xff0c;它基于nodejs平台。 Express 简介 Express是一个简洁而灵活的node.js Web应用框架, 提供了一系列强大特性帮助你创建各种Web应用&#xff0c;和丰富的HTTP工具。 使用Express可以快速地搭建一个完整功能的网站。 Expre…...

从零实现富文本编辑器#5-编辑器选区模型的状态结构表达

先前我们总结了浏览器选区模型的交互策略&#xff0c;并且实现了基本的选区操作&#xff0c;还调研了自绘选区的实现。那么相对的&#xff0c;我们还需要设计编辑器的选区表达&#xff0c;也可以称为模型选区。编辑器中应用变更时的操作范围&#xff0c;就是以模型选区为基准来…...

Mybatis逆向工程,动态创建实体类、条件扩展类、Mapper接口、Mapper.xml映射文件

今天呢&#xff0c;博主的学习进度也是步入了Java Mybatis 框架&#xff0c;目前正在逐步杨帆旗航。 那么接下来就给大家出一期有关 Mybatis 逆向工程的教学&#xff0c;希望能对大家有所帮助&#xff0c;也特别欢迎大家指点不足之处&#xff0c;小生很乐意接受正确的建议&…...

为什么需要建设工程项目管理?工程项目管理有哪些亮点功能?

在建筑行业&#xff0c;项目管理的重要性不言而喻。随着工程规模的扩大、技术复杂度的提升&#xff0c;传统的管理模式已经难以满足现代工程的需求。过去&#xff0c;许多企业依赖手工记录、口头沟通和分散的信息管理&#xff0c;导致效率低下、成本失控、风险频发。例如&#…...

C++中string流知识详解和示例

一、概览与类体系 C 提供三种基于内存字符串的流&#xff0c;定义在 <sstream> 中&#xff1a; std::istringstream&#xff1a;输入流&#xff0c;从已有字符串中读取并解析。std::ostringstream&#xff1a;输出流&#xff0c;向内部缓冲区写入内容&#xff0c;最终取…...

C++.OpenGL (10/64)基础光照(Basic Lighting)

基础光照(Basic Lighting) 冯氏光照模型(Phong Lighting Model) #mermaid-svg-GLdskXwWINxNGHso {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-GLdskXwWINxNGHso .error-icon{fill:#552222;}#mermaid-svg-GLd…...

Docker 本地安装 mysql 数据库

Docker: Accelerated Container Application Development 下载对应操作系统版本的 docker &#xff1b;并安装。 基础操作不再赘述。 打开 macOS 终端&#xff0c;开始 docker 安装mysql之旅 第一步 docker search mysql 》〉docker search mysql NAME DE…...

初探Service服务发现机制

1.Service简介 Service是将运行在一组Pod上的应用程序发布为网络服务的抽象方法。 主要功能&#xff1a;服务发现和负载均衡。 Service类型的包括ClusterIP类型、NodePort类型、LoadBalancer类型、ExternalName类型 2.Endpoints简介 Endpoints是一种Kubernetes资源&#xf…...

C#中的CLR属性、依赖属性与附加属性

CLR属性的主要特征 封装性&#xff1a; 隐藏字段的实现细节 提供对字段的受控访问 访问控制&#xff1a; 可单独设置get/set访问器的可见性 可创建只读或只写属性 计算属性&#xff1a; 可以在getter中执行计算逻辑 不需要直接对应一个字段 验证逻辑&#xff1a; 可以…...

MySQL JOIN 表过多的优化思路

当 MySQL 查询涉及大量表 JOIN 时&#xff0c;性能会显著下降。以下是优化思路和简易实现方法&#xff1a; 一、核心优化思路 减少 JOIN 数量 数据冗余&#xff1a;添加必要的冗余字段&#xff08;如订单表直接存储用户名&#xff09;合并表&#xff1a;将频繁关联的小表合并成…...