字段更改的休眠审计日志

Posted

技术标签:

【中文标题】字段更改的休眠审计日志【英文标题】:Hibernate audit log of fields' change 【发布时间】:2019-06-26 19:59:01 【问题描述】:

如何将实体的更改记录到日志文件中? 考虑一下我有这样的Person

import org.hibernate.envers.Audited;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.GeneratedValue;
import javax.persistence.Column;

@Entity
@Audited
public class Person 
    @Id
    @GeneratedValue
    private int id;

    private String name;
    private String surname;
// add getters, setters, constructors, equals and hashCode here

以及更改现有Person的代码

Person p1 = new Person("name-1", "surname-1");
personRepository.save(p1);
Person p2 = personRepository.findOne(1L);
p2.setName("new-name");
personRepository.save(p2);

我怎样才能拥有

旧实体 新实体 字段列表已更改(类似于Diffable 的结果)

在我的日志文件中?我知道 envars 可以将更改存储在 db 中,然后让我使用 AuditReader 提取它们,但我喜欢将更改存储在 Json 文件中以将它们发送到第三方应用程序(如 Elastic)。

【问题讨论】:

弹性搜索必须作为当前事务的一部分立即更新,还是您能够以最终一致性模式更新弹性搜索? 在一个最终的过程中。实际上我喜欢登录一个文件,这样 LogStash 就可以读取它并推送到 Elastic 中。 你的问题太宽泛了。不如让 Envers 完成它的工作,然后在审计表中构建一些 JSON 表示的数据。 问题是我不喜欢 Envers 给我的数据库增加更多负载。此外,Envers 强迫我查询我的数据库以获取更改。这意味着我可怜的数据库会承受更多负载。 【参考方案1】:

我实际上会从两个角度来解决这个问题。

使用 Envers 获得的好处之一是您可以非常快速地注释实体并准确告诉框架您希望如何跟踪实体模型的更改。更好的是,您可以让 Envers 自动为您生成 diffable 字段。

让我们来看看这个基本实体:

@Entity
@Audited(withModifiedFlag = true)
public class Person 
  @Id
  @GeneratedValue
  private Integer id;
  private String name;

当您启用withModifiedFlag 功能时,这会通知 Envers 向该实体的审计表中添加一些额外的元数据列,因此该实体的审计表基本上如下所示:

+----+------+----------+-----+---------+
| ID | name | name_MOD | REV | REVTYPE |
+----+------+----------+-----+---------+

这里的好处是,如果您使用某些流程直接从表中导出和流式传输数据,您不再需要实际区分当前行与前一行来了解发生了什么变化;只需查看关联的 _MOD 列是 1 (true) 还是 0 (false),架构就会自动告诉您这一点。

从这一点来看,您有几个选择。

本机查询 ETL

您可以使用 Hibernate 原生查询通过某个后台应用程序线程或单独的后台进程来提取数据并将其转换为 ES 的 JSON。由于_MOD 列为您提供了字段已更改的指示符,因此您可以轻松读取行并构建必要的数据,而无需在提取时执行差异操作。

我还建议配置 Envers 以将审计对象放在单独的目录/模式中。这最大限度地提高了数据库同时改进跨多个数据库的磁盘 IO 的能力。

Debezium ETL

Debezium 项目是处理数据复制的绝佳方式。它的强大优势之一是它使用户能够跨异构平台执行此操作,从而完全适合您的模型。

这里最大的不同是 Debezium 不直接读取数据库来确定更改,而是读取数据库事务日志文件并生成一系列事件来描述针对该数据库发生的 DML 操作。简而言之,您避免了您非常关心的读取操作,因为 Debezium 直接从事务日志中重新水化状态。

举个例子:

    Hibernate 执行INSERT INTO Person (?,?) VALUES (?,?)。 Hibernate Envers 执行 INSERT INTO Person_AUD (...) VALUES (....) 数据库将该操作写入重做/事务日志。 Debezium 注意到日志已写入,然后读取条目。 Debezium 为Person_AUD(订阅的表)生成插入事件。 该事件的任何注册相关方都会收到并处理它。

在 (5) 中,您的转换/加载代码将存在以接收该插入事件并生成 JSON 输出并将其发送到 ES。

总结

通过使用 Debezium,您能够以极其高效的方式离线跨异构环境复制数据。该项目不仅非常适合您的用例,而且在当今微服务架构的现代世界中,服务之间的数据共享至关重要。

通过使用 Envers,您可以提供在线回退解决方案,以便在您的 ES 集群不可用或过载时为用户提供审计历史数据,而不是为用户提供“服务不可用,稍后再回来”回应。

无论您做出什么决定,性能都不是唯一令人担忧的因素。您还应该注意用户体验、可扩展性和可靠性;因此,为什么我认为最佳解决方案是将两者配对。

【讨论】:

我喜欢你的想法,我在旧项目中使用过 Debezium。但是,目前,我无法将新技术添加到项目堆栈中。任务的定义生动地迫使我将差异记录在日志文件中。【参考方案2】:

您可以通过实现org.hibernate.EmptyInterceptor 来编写自定义拦截器。这具有使用实体的新旧快照更新/插入/删除的回调。

更多详情请参考this文章

【讨论】:

我有零钱吗?我只看到应该刷新到 db 的实体。 @mLotfizad 因为你有新旧状态,你可以计算差异

以上是关于字段更改的休眠审计日志的主要内容,如果未能解决你的问题,请参考以下文章

EF Core 如何实现对值对象更改的审计日志

LinqToSQL 和审计更改的字段

从休眠审计历史中选择一个字段

如何在数据库中保留修改的历史细节(审计跟踪)?

如何在 asp.net mvc 中创建审计日志/审计跟踪

为 MySQL 表实现审计