【问题标题】:Using @Version with Envers将 @Version 与 Envers 一起使用
【发布时间】:2018-03-03 03:39:58
【问题描述】:

我的应用程序使用 SpringBoot、Hibernate 和 Spring Data JPA 来实现奇迹。我正在尝试将 Hibernate 的 Envers 库添加到其中,以进行修订跟踪。但是,我在 Envers 和 @Version 注释之间遇到了一些冲突。

默认情况下,Envers 不会审核任何带有@Version 注释的字段。这对我来说很有意义,因为@Version 只是为了跟踪乐观锁定,所以我真的不需要保留它的修订历史。

但是,我遇到的问题是,当我获得修订实体时,它不会随版本返回。这是有道理的 - Envers 将修订实体存储在 ..._AUD 表中,并且该表没有 Version 列,因此显然该实体不会有 Version。问题是我有时想将该实体用作另一个事务的一部分*,但由于它没有版本,因此该实体被视为瞬态并引发异常。

所以我有几个不同的解决方案,但似乎都不是理想的,所以我希望就我能做到这一点的最佳方式获得一些意见:

  1. 我可以获取修订实体,然后使用修订实体中的 id 进行单独的存储库调用以获取实际实体,然后仅使用该实体。这会增加很多复杂性(尤其是在确定要使用的正确存储库时)和似乎不必要的额外数据库调用。

  2. 我可以保存版本。这并不理想,因为它不是我真正关心的信息,但如果它意味着一个有效的、有效的系统,我可以处理它。我也试过这个并且遇到了问题(在我的配置中将 doNotAuditOptimsticLockingField 设置为 true 似乎没有任何作用),但如果确定这是最好的解决方案,我将打开一个单独的问题来处理它。

  3. 我可能会更改未能使用 ID 而不是实际实体的存储库调用,但如果我必须更改许多其他存储库调用以使其工作,我不会感到惊讶.

如果有第四个选项,我会全力以赴!

*:另一个事务是在将实体转换为要由 REST 端点返回的资源时发生的数据库调用。我拥有的实体之一是 AccountEntity。我得到的修订实体就是被这个方法使用的:

LinkOwnerEntity findByAccountEntity(AccountEntity accountEntity);

linkOwner Entity 类看起来像这样:

public class LinkOwnerEntity {

    ...

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "ACCOUNT_ID", unique=true, foreignKey = @ForeignKey(name = "FK_OWNER_ACCOUNT_ID"))
    private AccountEntity accountEntity;

    ...

}

【问题讨论】:

    标签: java hibernate jpa spring-boot hibernate-envers


    【解决方案1】:

    乐观锁定就是确保您看到的数据就是您要修改的数据。所以当用户决定在他的屏幕上恢复一个旧版本(我想这就是你想用 Envers 做的)时,他应该看到内容的当前版本和他想要恢复的内容的旧版本。您需要将用户看到的版本的版本字段保留为当前版本,并在您的请求中将其发送给应用程序并用于保存修改。

    这样,如果数据在用户查看后已被修改,他的修改将被拒绝,而不是覆盖其他人的修改。

    【讨论】:

    • 我对这里所需功能的理解只是用户需要读取数据,而不是修改或恢复它。这将并且可以通过通常可用的途径单独完成。不过,您确实提出了一个很好的观点,我将验证不需要此功能。但是,如果不需要,我认为没有其他需要 Version 字段。
    • 用户代码真的不应该有使用@Version属性做任何事情的习惯。
    • 如果这里的最终目标是恢复一个实体,那应该归结为首先要求 Hibernate 基于 PK 提供一个托管实体(如果当前仍然存在),然后使用该 PK要求 Envers 进行具体修订。然后 OP 需要将所有 changed 属性覆盖到托管实例中,然后合并。如果实体已被删除并且不使用自然键,那么版本的概念在这里无关紧要,因为新实例将被分配一个新的代理键,并将被完全视为一个新实体。
    【解决方案2】:

    在深入探讨我认为您应该做什么之前,我确实想澄清一些事情。

    首先,在任何情况下,您都不应该将 Envers Query API 提供给您的实例视为分离的实体实例。

    根据您注释实体的方式,您从 Envers 检索的实例很可能是经过审计的历史行数据和当前行表数据的混合。实例是您的真实实体实例管理的属性的子集也是可行的,因为您可能只选择跟踪特定属性。

    因此,我个人不会考虑将检索到的实例用作与事务中的托管实体相关联的东西,至少不会盲目。

    其次,您的问题不一定清楚的是,首先查询 Envers 的真正目的,然后将结果用作映射中的关联,而不是首先简单地对真实实体执行查询.

    所以最终这让我想到了我的建议,这将是您的选项 3 或它的变体。

    听起来您可能想考虑使用Session#loadEntityManager#getReference。这两种方法旨在基于给定标识符构建代理实例,然后您可以在关联映射期间盲目地使用该代理,而无需直接为实例补水(从而避免数据库调用开销)。

    这种方法还避免了在此处使用 Envers Query API 的任何需要,因为根据您在问题中提供的内容,我完全看不出它在这个用例中的相关性。如果有特定原因涉及 Envers 查询,请更新您的问题。

    【讨论】:

      猜你喜欢
      • 2012-12-13
      • 1970-01-01
      • 2017-07-25
      • 1970-01-01
      • 1970-01-01
      • 2015-04-08
      • 1970-01-01
      • 2023-04-10
      • 1970-01-01
      相关资源
      最近更新 更多