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

数据留痕的方法

     在项目中,数据变更时,经常需要记录上次的数据,以便查看对比,专业术语叫做数据留痕。数据变更留痕(即记录数据的变更历史)是一个常见的需求,例如在审计、追踪数据变化或满足合规性要求的场景中。以下是数据留痕几种常见的实现方式:


1. 手动记录变更日志

在业务代码中手动记录数据变更的日志,将变更前后的数据保存到日志表或日志文件中。

实现步骤:

  1. 在数据变更的地方(如更新、删除操作)手动记录变更前后的数据。

  2. 将变更信息保存到数据库的日志表或日志文件中。

示例代码:

public class UserService {@Autowiredprivate UserRepository userRepository;@Autowiredprivate AuditLogRepository auditLogRepository;public void updateUser(User newUser) {// 获取旧数据User oldUser = userRepository.findById(newUser.getId()).orElseThrow();// 更新数据userRepository.save(newUser);// 记录变更日志AuditLog auditLog = new AuditLog();auditLog.setAction("UPDATE");auditLog.setEntityName("User");auditLog.setEntityId(newUser.getId());auditLog.setOldValue(oldUser.toString()); // 旧数据auditLog.setNewValue(newUser.toString()); // 新数据auditLog.setChangeTime(new Date());auditLogRepository.save(auditLog);}
}

优点:

  • 实现简单,直接控制日志内容。

  • 灵活性高,可以根据需求定制日志格式。

缺点:

  • 代码侵入性强,需要在每个变更点手动添加日志记录。

  • 容易遗漏,维护成本较高。


2. 使用AOP(面向切面编程)

通过AOP在数据变更的方法上添加切面,自动记录变更日志。

实现步骤:

  1. 定义一个切面,拦截数据变更的方法(如updatedelete)。

  2. 在切面中获取方法的参数和返回值,记录变更前后的数据。

示例代码:

@Aspect
@Component
public class DataChangeAspect {@Autowiredprivate AuditLogRepository auditLogRepository;@AfterReturning(pointcut = "execution(* com.example.service.UserService.updateUser(..))", returning = "result")public void logDataChange(JoinPoint joinPoint, Object result) {Object[] args = joinPoint.getArgs();User newUser = (User) args[0]; // 获取新数据User oldUser = (User) result;  // 获取旧数据// 记录变更日志AuditLog auditLog = new AuditLog();auditLog.setAction("UPDATE");auditLog.setEntityName("User");auditLog.setEntityId(newUser.getId());auditLog.setOldValue(oldUser.toString()); // 旧数据auditLog.setNewValue(newUser.toString()); // 新数据auditLog.setChangeTime(new Date());auditLogRepository.save(auditLog);}
}

优点:

  • 代码侵入性低,集中管理日志逻辑。

  • 灵活,可以根据需求定制切面。

缺点:

  • 需要熟悉AOP编程。

  • 可能增加系统复杂性。

3. 使用数据库触发器

通过数据库触发器在数据变更时自动记录历史数据。

实现步骤:

  1. 在数据库中创建触发器,监听目标表的变更(如 INSERTUPDATEDELETE)。

  2. 在触发器中将变更前后的数据插入到历史表中。

示例 SQL:

CREATE TABLE users_history (id INT PRIMARY KEY AUTO_INCREMENT,user_id INT,name VARCHAR(255),email VARCHAR(255),action VARCHAR(10),change_time TIMESTAMP
);CREATE TRIGGER trg_user_history
AFTER UPDATE ON users
FOR EACH ROW
BEGININSERT INTO users_history (user_id, name, email, action, change_time)VALUES (OLD.id, OLD.name, OLD.email, 'UPDATE', NOW());
END;

优点:

  • 与应用程序解耦,数据库层面实现。

  • 无需修改业务代码。

缺点:

  • 触发器可能影响数据库性能。

  • 调试和维护复杂。


4. 使用事件监听机制

通过 Spring 的事件监听机制,在数据变更时发布事件并记录日志。

实现步骤:

  1. 定义一个事件类(如 DataChangeEvent)。

  2. 在数据变更的地方发布事件。

  3. 监听事件并记录日志。

示例代码:

事件类:
@Data
public class DataChangeEvent {private String entityName;private Long entityId;private String oldValue;private String newValue;}
发布事件:
@Service
public class UserService {@Autowiredprivate ApplicationEventPublisher eventPublisher;public void updateUser(User newUser) {User oldUser = userRepository.findById(newUser.getId()).orElseThrow();userRepository.save(newUser);// 发布事件DataChangeEvent event = new DataChangeEvent("User", newUser.getId(), oldUser.toString(), newUser.toString());eventPublisher.publishEvent(event);}
}
监听事件:
@Component
public class DataChangeListener {@Autowiredprivate AuditLogRepository auditLogRepository;@EventListenerpublic void handleDataChangeEvent(DataChangeEvent event) {AuditLog auditLog = new AuditLog();auditLog.setAction("UPDATE");auditLog.setEntityName(event.getEntityName());auditLog.setEntityId(event.getEntityId());auditLog.setOldValue(event.getOldValue());auditLog.setNewValue(event.getNewValue());auditLog.setChangeTime(new Date());auditLogRepository.save(auditLog);}
}

优点:

  • 解耦业务逻辑和日志记录。

  • 灵活,支持异步处理。

缺点:

  • 需要熟悉 Spring 事件机制。

  • 可能增加系统复杂性。


5.总结

方式优点缺点适用场景
手动记录日志简单直接,灵活性高代码侵入性强,维护成本高小型项目,简单需求
AOP代码侵入性低,集中管理日志逻辑需要熟悉 AOP,可能增加复杂性需要集中管理日志的中大型项目
数据库触发器与应用程序解耦,无需修改代码调试复杂,可能影响性能数据库层面的审计需求
事件监听机制解耦业务逻辑,支持异步处理需要熟悉 Spring 事件机制需要解耦和异步处理的场景

    根据项目需求和技术栈选择合适的方式。

    如果项目使用 Hibernate,推荐使用 Envers;如果需要解耦业务逻辑,可以使用 AOP 或事件监听机制;如果希望与应用程序解耦,可以使用数据库触发器。

相关文章:

数据留痕的方法

在项目中,数据变更时,经常需要记录上次的数据,以便查看对比,专业术语叫做数据留痕。数据变更留痕(即记录数据的变更历史)是一个常见的需求,例如在审计、追踪数据变化或满足合规性要求的场景中。…...

机器学习数学基础:19.线性相关与线性无关

一、线性相关与线性无关的定义 (一)线性相关 想象我们有一组向量,就好比是一群有着不同“力量”和“方向”的小伙伴。给定的向量组 α ⃗ 1 , α ⃗ 2 , ⋯ , α ⃗ m \vec{\alpha}_1, \vec{\alpha}_2, \cdots, \vec{\alpha}_m α 1​,α 2…...

ArgoCD实战指南:GitOps驱动下的Kubernetes自动化部署与Helm/Kustomize集成

摘要 ArgoCD 是一种 GitOps 持续交付工具,专为 Kubernetes 设计。它能够自动同步 Git 仓库中的声明性配置,并将其应用到 Kubernetes 集群中。本文将介绍 ArgoCD 的架构、安装步骤,以及如何结合 Helm 和 Kustomize 进行 Kubernetes 自动化部署。 引言 为什么选择 ArgoCD?…...

JVM虚拟机以及跨平台原理

相信大家已经了解到Java具有跨平台的特性,即“一次编译,到处运行”,例如在Windows下编写的程序,无需任何修改就可以在Linux下运行,这是C和C很难做到的。 那么,跨平台是怎样实现的呢?这就要谈及…...

【AIGC提示词系统】基于 DeepSeek R1 + ClaudeAI 易经占卜系统

上篇因为是VIP,这篇来一个免费的 提示词在最下方,喜欢的点个关注吧 引言 在人工智能与传统文化交融的今天,如何让AI系统能够传递传统易经文化的智慧,同时保持易经本身的神秘感和权威性,是一个极具挑战性的课题。本文将…...

电路笔记 : opa 运放失调电压失调电流输入偏置电流 + 反向放大器的平衡电阻 R3 = R1 // R2 以减小输出直流噪声

目录 定义影响和解决失调电压输入偏置电流平衡电阻R3推导公式: 失调电流 实际的运算放大器(Op-Amp)存在一些非理想特性,如失调电压(VIO)、失调电流(IIO)和输入偏置电流(I…...

ScrapeGraphAI颠覆传统网络爬虫技术

ScrapeGraphAI颠覆传统网络爬虫技术! 引言 在互联网时代,数据如同油田,丰富而深邃。但如何有效地提取这些数据,仍然是许多开发者面临的艰巨任务。你有没有想过,传统的网络爬虫技术是否已经过时?如今&…...

通过多层混合MTL结构提升股票市场预测的准确性,R²最高为0.98

“Boosting the Accuracy of Stock Market Prediction via Multi-Layer Hybrid MTL Structure” 论文地址:https://arxiv.org/pdf/2501.09760 ​​​​​​​ 摘要 本研究引入了一种创新的多层次混合多任务学习架构,致力于提升股市预测的效能。此架构融…...

java将list转成树结构

首先是实体类 public class DwdCusPtlSelectDto {//idprivate String key;//值private String value;//中文名private String title;private List<DwdCusPtlSelectDto> children;private String parentId;public void addChild(DwdCusPtlSelectDto child) {if(this.chil…...

互联网分布式ID解决方案

业界实现方案 1. 基于UUID 2. 基于DB数据库多种模式(自增主键、segment) 3. 基于Redis 4. 基于ZK、ETCD 5. 基于SnowFlake 6. 美团Leaf(DB-Segment、zkSnowFlake) 7. 百度uid-generator() 基于UUID生成唯一ID UUID生成策略 推荐阅读 DDD领域驱动与微服务架构设计设计模…...

xinference 安装(http导致错误解决)

为什么要使用xinference 安装xinference 环境 1&#xff09;conda create -n Xinference python3.11 注意&#xff1a;3.9 3.10均可能出现xinference 安装时候出现numpy兼容性&#xff0c;以及无法安装all版本 错误&#xff1a; error while attempting to bind on address&am…...

334递增的三元子序列贪心算法(思路解析+源码)

文章目录 题目思路解析源码总结题目 思路解析 有两种解法:解法一:动态规划(利用dp找到数组最长递增序列长度,判断是否大于3即可)本题不适用,因为时间复杂度为O(n^2),超时。 解法二:贪心算法:解法如上图,题目要求长度为三,设置第一个元素为长度1的值,是指长度二的…...

【Linux】29.Linux 多线程(3)

文章目录 8.4 生产者消费者模型8.4.1 为何要使用生产者消费者模型8.4.2 生产者消费者模型优点 8.5 基于BlockingQueue的生产者消费者模型8.5.1 C queue模拟阻塞队列的生产消费模型 8.6. 为什么pthread_cond_wait 需要互斥量?8.7 条件变量使用规范8.8 条件变量的封装8.9 POSIX信…...

利用UNIAPP实现短视频上下滑动播放功能

在 UniApp 中实现一个短视频上下滑动播放的功能,可以使用 swiper 组件来实现滑动效果,并结合 video 组件来播放短视频。以下是一个完整的示例,展示如何在 UniApp 中实现这一功能。 1. 创建 UniApp 项目 如果你还没有创建 UniApp 项目,可以使用 HBuilderX 创建一个新的项目…...

vscode+CMake+Debug实现 及权限不足等诸多问题汇总

环境说明 有空再补充 直接贴两个json tasks.json {"version": "2.0.0","tasks": [{"label": "cmake","type": "shell","command": "cmake","args": ["../"…...

【提示词工程】探索大语言模型的参数设置:优化提示词交互的技巧

在与大语言模型(Large Language Model, LLM)进行交互时,提示词的设计和参数设置直接影响生成内容的质量和效果。无论是通过 API 调用还是直接使用模型,掌握模型的参数配置方法都至关重要。本文将为您详细解析常见的参数设置及其应用场景,帮助您更高效地利用大语言模型。 …...

基于 .NET 8.0 gRPC通讯架构设计讲解,客户端+服务端

目录 1.简要说明 2.服务端设计 2.1 服务端创建 2.2 服务端设计 2.3 服务端业务模块 3.客户端设计-控制台 4.客户端设计-Avalonia桌面程序 5.客户端设计-MAUI安卓端程序 1.简要说明 gRPC 一开始由 google 开发&#xff0c;是一款语言中立、平台中立、开源的远程过程调用…...

6.Centos7上部署flask+SQLAlchemy+python+达梦数据库

情况说明 前面已经介绍了window上使用pycharm工具开发项目时,window版的python连接达梦数据库需要的第三方包。 这篇文章讲述,centos7上的python版本连接达梦数据库需要的第三方包。 之前是在windows上安装达梦数据库的客户端,将驱动包安装到windows版本的python中。(开…...

【C语言系列】深入理解指针(5)

深入理解指针&#xff08;5&#xff09; 一、sizeof和strlen的对比1.1sizeof1.2strlen1.3sizeof和strlen的对比 二、数组和指针笔试题解析2.1 一维数组2.2 字符数组2.2.1代码1&#xff1a;2.2.2代码2&#xff1a;2.2.3代码3&#xff1a;2.2.4代码4&#xff1a;2.2.5代码5&#…...

mysql自连接 处理层次结构数据

MySQL 的自连接&#xff08;Self Join&#xff09;是一种特殊的连接方式&#xff0c;它允许一个表与自身进行连接。自连接通常用于处理具有层次结构或递归关系的数据&#xff0c;或者当同一张表中的数据需要相互关联时。以下是几种常见的场景&#xff0c;说明何时应该使用自连接…...

Golang 面试经典题:map 的 key 可以是什么类型?哪些不可以?

Golang 面试经典题&#xff1a;map 的 key 可以是什么类型&#xff1f;哪些不可以&#xff1f; 在 Golang 的面试中&#xff0c;map 类型的使用是一个常见的考点&#xff0c;其中对 key 类型的合法性 是一道常被提及的基础却很容易被忽视的问题。本文将带你深入理解 Golang 中…...

spring:实例工厂方法获取bean

spring处理使用静态工厂方法获取bean实例&#xff0c;也可以通过实例工厂方法获取bean实例。 实例工厂方法步骤如下&#xff1a; 定义实例工厂类&#xff08;Java代码&#xff09;&#xff0c;定义实例工厂&#xff08;xml&#xff09;&#xff0c;定义调用实例工厂&#xff…...

OPenCV CUDA模块图像处理-----对图像执行 均值漂移滤波(Mean Shift Filtering)函数meanShiftFiltering()

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 在 GPU 上对图像执行 均值漂移滤波&#xff08;Mean Shift Filtering&#xff09;&#xff0c;用于图像分割或平滑处理。 该函数将输入图像中的…...

Mobile ALOHA全身模仿学习

一、题目 Mobile ALOHA&#xff1a;通过低成本全身远程操作学习双手移动操作 传统模仿学习&#xff08;Imitation Learning&#xff09;缺点&#xff1a;聚焦与桌面操作&#xff0c;缺乏通用任务所需的移动性和灵活性 本论文优点&#xff1a;&#xff08;1&#xff09;在ALOHA…...

【生成模型】视频生成论文调研

工作清单 上游应用方向&#xff1a;控制、速度、时长、高动态、多主体驱动 类型工作基础模型WAN / WAN-VACE / HunyuanVideo控制条件轨迹控制ATI~镜头控制ReCamMaster~多主体驱动Phantom~音频驱动Let Them Talk: Audio-Driven Multi-Person Conversational Video Generation速…...

Mysql中select查询语句的执行过程

目录 1、介绍 1.1、组件介绍 1.2、Sql执行顺序 2、执行流程 2.1. 连接与认证 2.2. 查询缓存 2.3. 语法解析&#xff08;Parser&#xff09; 2.4、执行sql 1. 预处理&#xff08;Preprocessor&#xff09; 2. 查询优化器&#xff08;Optimizer&#xff09; 3. 执行器…...

2025年渗透测试面试题总结-腾讯[实习]科恩实验室-安全工程师(题目+回答)

安全领域各种资源&#xff0c;学习文档&#xff0c;以及工具分享、前沿信息分享、POC、EXP分享。不定期分享各种好玩的项目及好用的工具&#xff0c;欢迎关注。 目录 腾讯[实习]科恩实验室-安全工程师 一、网络与协议 1. TCP三次握手 2. SYN扫描原理 3. HTTPS证书机制 二…...

【Nginx】使用 Nginx+Lua 实现基于 IP 的访问频率限制

使用 NginxLua 实现基于 IP 的访问频率限制 在高并发场景下&#xff0c;限制某个 IP 的访问频率是非常重要的&#xff0c;可以有效防止恶意攻击或错误配置导致的服务宕机。以下是一个详细的实现方案&#xff0c;使用 Nginx 和 Lua 脚本结合 Redis 来实现基于 IP 的访问频率限制…...

jmeter聚合报告中参数详解

sample、average、min、max、90%line、95%line,99%line、Error错误率、吞吐量Thoughput、KB/sec每秒传输的数据量 sample&#xff08;样本数&#xff09; 表示测试中发送的请求数量&#xff0c;即测试执行了多少次请求。 单位&#xff0c;以个或者次数表示。 示例&#xff1a;…...

LabVIEW双光子成像系统技术

双光子成像技术的核心特性 双光子成像通过双低能量光子协同激发机制&#xff0c;展现出显著的技术优势&#xff1a; 深层组织穿透能力&#xff1a;适用于活体组织深度成像 高分辨率观测性能&#xff1a;满足微观结构的精细研究需求 低光毒性特点&#xff1a;减少对样本的损伤…...