首页 > 解决方案 > Hibernate Envers 新旧值

问题描述

我有一张如下表。

╔════╦══════════════╦══════╗
║ Id ║     User     ║ Age  ║
╠════╬══════════════╬══════╣
║  1 ║ Foo Bar      ║  35  ║
║  2 ║ Bar Foo      ║  40  ║
║  3 ║ Bob Dixon    ║  50  ║
║  4 ║ Alice Spolsky║  59  ║
╚════╩══════════════╩══════╝

现在这些是初始值,我想使用 Hibernate Envers 存储审计,包括所有旧值和新值。默认配置让我只存储新值,因此我错过了第一个创建对象的值。例如,如果我说将第一个年龄更新为 55,那么 aud 表将如下所示(不包括详细信息列):

╔════╦══════════════╦══════╗
║ Id ║     User     ║ Age  ║
╠════╬══════════════╬══════╣
║  1 ║ Foo Bar      ║  55  ║
╚════╩══════════════╩══════╝

在此之后我不知道初始值是否为 35。我需要的是下面的审计表:

╔════╦══════════════╦══════════╦═════════╗
║ Id ║     User     ║ OldAge   ║ New Age ║
╠════╬══════════════╬══════════╬═════════╣
║  1 ║ Foo Bar      ║  35      ║   55    ║
╚════╩══════════════╩══════════╩═════════╝

如何使用 Hibernate Envers 实现此类审计?

标签: hibernatejpaspring-data-jpahibernate-envers

解决方案


看起来你可能是

  1. 手动播种数据库并绕过 Hibernate
  2. 为已有数据的实体映射启用 Hibernate Envers

为了让 Envers 响应更改,通过 Hibernate 完成数据库更改至关重要,以便其事件框架触发所有负责生成审计历史的 Envers 侦听器。如果任何步骤绕过这一点,将不会生成审计条目。

Envers 也不会为已有数据的实体映射生成审计历史。期望是您的实体表是空的,并且插入、更新和删除事件将被 Hibernate 捕获,传播到 Envers,并且审计历史将基于这些变化。对于现有数据,没有事件,因此不会自动生成审计历史记录。如果您打算对具有现有数据的表启用审计,则由实施者正确地播种审计历史记录。

因此,如果我们退后一步,假设您没有在 ORM 基表中已存在数据的实体上手动播种或启用 Envers,那么您的审计表在更新后应该如下所示:

+---+--------------------+-----+-----+---------+
|Id | User               | Age | REV | REVTYPE |
+---+--------------------+-----+-----+---------+
| 1 | Foo Bar            |  35 |   1 |       0 |
| 2 | Bar Foo            |  40 |   1 |       0 |
| 3 | Bob Dixon          |  50 |   1 |       0 |
| 4 | Alice Spolsky      |  59 |   1 |       0 |
| 1 | Foo Bar            |  55 |   2 |       1 |
+---+--------------------+-----+-----+---------+

您会注意到该列REVTYPE。此列表示与行突变相关的操作。这些值是枚举类型的一部分,其中:

  • 0 = 插入
  • 1 = 更新
  • 2 = 删除

Envers 查询 API 允许您获取给定实体主键的修订号列表,然后您可以从那里获取每个修订并使用任何差异库执行对象实例之间的差异或编写您自己的。

Envers 还支持一个名为withModifiedFlags. 默认情况下禁用此功能,因为它为实体模型添加了大量额外的支持列,但启用后,您的模型实际上如下所示:

+---+--------------------+----------+-----+---------+-----+---------+
|Id | User               | User_MOD | Age | Age_MOD | REV | REVTYPE |
+---+--------------------+----------+-----+---------+-----+---------+
| 1 | Foo Bar            |        1 | 35  |       1 |   1 |       0 |
| 2 | Bar Foo            |        1 | 40  |       1 |   1 |       0 |
| 3 | Bob Dixon          |        1 | 50  |       1 |   1 |       0 |
| 4 | Alice Spolsky      |        1 | 59  |       1 |   1 |       0 |
| 1 | Foo Bar            |        0 | 55  |       1 |   2 |       1 |
+---+--------------------+----------+-----+---------+-----+---------+

这些_MOD列很有用,因为它允许您构建复杂的查询来检查某些感兴趣的列是否在修订中发生了变化。虽然它不允许您从当前审计行中获取先前的值,但可以通过查看该实体主键的审计表历史来推断该信息。

您可能会问,如果 Envers 可以添加这些_MOD列,为什么不能添加这些“先验值”列。理论上我们可以做到这一点,但我认为退后一步并衡量这是否真的有价值很重要。

我认为以这样一种方式增强查询 API 会更有价值,即用户可以要求 Envers 生成实体历史的某种表示,可能是两个具体修订之间的差异,或者可能是两个修订之间的生命周期。然后,此表示将允许您从中获取先前值和当前值。

简而言之,不需要更改架构,因为 API 会完全为您执行此操作。


推荐阅读