字段更改的 Hibernate 审计日志

Hibernate audit log of fields' change

如何将实体的更改记录到日志文件中? 考虑一下我有这样的 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);

我怎样才能拥有

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

您可以通过实现 org.hibernate.EmptyInterceptor 来编写自定义拦截器。这具有对 update/insert/delete 的回调,其中包含实体的新旧快照。

有关详细信息,请参阅 this 文章

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

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

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

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

当您启用 withModifiedFlag 功能时,这会通知 Envers 添加一些额外的元数据列到该实体的审计 table 中,因此基本上该实体的审计 table 看起来像这个:

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

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

从现在开始,您有几个选择。

本机查询 ETL

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

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

Debezium ETL

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

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

举个例子:

  1. Hibernate 执行 INSERT INTO Person (?,?) VALUES (?,?).
  2. Hibernate Envers 执行 INSERT INTO Person_AUD (...) VALUES (....)
  3. 数据库将该操作写入 redo/transaction 日志。
  4. Debezium 注意到日志已写入,读取条目。
  5. Debezium 为 Person_AUD(table 订阅)生成一个插入事件。
  6. 该事件的任何注册相关方都会收到并处理它。

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

总结

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

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

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