【问题标题】:Hibernate Envers find in OneToMany JoinTable在 OneToMany JoinTable 中找到 Hibernate Envers
【发布时间】:2017-07-24 14:37:49
【问题描述】:

您好,我强迫 Envers 出问题。每个类都扩展了包含@GeneratedAuto id 等的 BaseEntity。有两个实体:

@Audited
@Entity
public class HandballInjury extends Injury {

  @OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
  @JoinTable(name = "HandballDictionaryFact")
  private List<HandballDictionaryFact> handballDictionaryFacts = new ArrayList<HandballDictionaryFact>();
  private HandballTimeOfInjury timeOfInjury;

  ...
}

还有

@Audited
@Entity
@Table(name = "handballDictionaryFact")
public class HandballDictionaryFact extends DictionaryFact{
  ...
}

*父母双方都有@Audited 注释。我的问题是,当我在 HandballInjury 上使用 AuditEntity 查找方法时,它会返回正确的伤害,其中包含属于伤害但不关心修订 ID 的所有 dictionaryFacts。在数据库中,一切都正确保存。我认为,如果表已连接,则 envers 只会查看 id 是否正确,而无需查看修订。 Hibernate Envers 版本:5.1.0.Final

编辑 1:

我发现我没有通过 SessionFactory.openSession() 打开新会话。因此,envers 正在进入 1 LevelCache 以获取数据。我已经修复了它,但它没有帮助。这就是查询的启动方式:

  result = getReader().find(HandballInjury.class, ((HandballInjury) object).getId(), revisionNumber);

也许我应该编写自己的查询?

我注意到下面的查询还返回 HandballDictionaryFacts_aud 中的所有实体,而不是指定的 revisionNumber。

 List<HandballDictionaryFact> dictionaryFact = getReader().createQuery()
                .forEntitiesAtRevision(HandballDictionaryFact.class, revisionNumber).getResultList();

编辑 2: 我发现 find 查询正在查找 RevNumber 小于或等于 handballInjury.RevNumber 的 handballDictionaryFact。我的问题是现在我可以强迫 envers 看起来只是平等的吗?部分查询:

        and handballdi1_.REV=(
        select
            max(handballdi2_.REV) 
        from
            dictionaryfact_AUD handballdi2_ 
        where
            handballdi2_.DTYPE='HandballDictionaryFact' 
            and handballdi2_.REV<=?
            and handballdi1_.id=handballdi2_.id
    ) 

第一次编辑的查询还会检查 revNumber 是否小于或等于。

【问题讨论】:

  • Envers 应该应用 REV 谓词,以便与聚合根的任何关联,在本例中为 HandballInjury,将等于或小于聚合根的关联。如果您绝对确定它没有这样做,请打开 JIRA 票证并使用重现错误的简单测试用例报告问题。
  • 调试后发现envers从firstlevelcache而不是数据库中获取数据。但不知道如何改变它。
  • 您是说它从 Session 的 1LC 或 Envers 的内部 1LC 获取数据,因为它们是两个不同的东西。您能否使用查询更新您的帖子以及您如何观察这种行为?

标签: java spring hibernate hibernate-envers


【解决方案1】:

我的问题是,当我在 HandballInjury 上使用 AuditEntity 查找方法时,它会返回正确的伤害,其中包含属于伤害的所有 dictionaryFacts 但不关心修订 ID。在数据库中,一切都正确保存。我认为如果表已连接,则 envers 只会查看 id 是否正确,而无需查看修订。

这是不可能的,因为审计跟踪的实际工作方式。

如果您查看审计表,您会注意到在许多情况下,实体的主键在审计表中添加了修订号,以允许同一实体主键的多个条目。

+----+-----+---------+---------+
| ID | REV | REVTYPE | DATA    |
+----+-----+---------+---------+
|  1 |   1 |       0 | Initial |
+----+-----+---------+---------+
|  1 |   2 |       1 | Updated |
+----+-----+---------+---------+
|  1 |   3 |       2 |         |
+----+-----+---------+---------+

正如您在此插图中看到的那样,此处的表有 3 行用于插入 (REVTYPE=0)、更新 (REVTYPE=1) 和删除 (REVTYPE=2) 实体的同一实体主键。

如果此表与另一个实体相关联,我们还需要考虑REV 值,以确保根据创建修订时的那个时间点关联是正确的。

让我们考虑另一个指向上表 ID=1 的实体。如果我们不考虑REV,那么我们不知道应该如何填充关联审计实体实例上的DATA 字段。应该是Initial 还是Updated

这是必须考虑REV 字段的另一个原因。

发现我没有通过 SessionFactory.openSession() 打开新会话。因此,envers 正在进入 1 LevelCache 以获取数据。我已经修复了它,但它没有帮助。

我认为在此澄清 Envers 使用自己的 L1C 实现很重要,该实现独立于 Hibernate Session 使用的实现。

Envers 使用它自己的实现,因为它实例化的对象不是真正的托管实体,而是只是构造的对象,这些对象使用实体对象布局来表示该实体在给定版本中的状态时间。

我不太清楚为什么您必须更改与会话管理相关的任何内容,除非您的代码正在做其他晦涩的事情。

HandballInjury injuryAtRevision = getReader()
  .find( HandballInjury.class, handballInjury.getId(), revisionNumber );

上面的查询基本上会查看 Envers L1C 是否包含给定主键和修订版的 HandballInjury 实例。如果是,则返回它;否则它会从数据库中获取它。

返回的实例将代表HandballInjury 在生成指定的revisionNumber 值时的状态,包括任何已审计的关联。

也许我应该编写自己的查询?

为什么,您在这里没有准确地阐明问题。如上所述,查询完全按照它的意图进行。

我注意到下面的查询还返回 HandballDictionaryFacts_aud 中的所有实体,而不是指定的 revisionNumber。

 List<HandballDictionaryFact> dictionaryFact = getReader().createQuery()
   .forEntitiesAtRevision( HandballDictionaryFact.class, revisionNumber )
   .getResultList();

这是预期的行为。

如果我们假设我们有两个事实,一个是在修订版 1 中创建的,另一个是在修订版 2 中创建的,并且上面使用了 revisionNumber=2,那么在那一刻,您定义了 2 个实体,不是吗?

我可以看到这里改进了 java-doc 以使其更清晰,但这仍然是预期的行为。

编辑 2:我发现 find 查询正在查找 RevNumber 小于或等于 handballInjury.RevNumber 的 handballDictionaryFact。我现在的问题是我可以强迫 envers 看起来只是平等

如果您希望明确并获取超出查询默认范围的特定修订版的实体,您可以添加额外的谓词以返回该类型的结果。

 List<HandballDictionaryFact> dictionaryFact = getReader().createQuery()
   .forEntitiesAtRevision( HandballDictionaryFact.class, revisionNumber )
   .add( AuditEntity.revisionNumber().eq( 2 ) )
   .getResultList();

采用与之前相同的假设,一个在修订版 1 中创建的事实和另一个在修订版 2 中创建的事实,这将返回一个包含在修订版 2 中专门创建的事实的单个条目。

【讨论】:

    猜你喜欢
    • 2013-03-15
    • 2018-10-14
    • 1970-01-01
    • 1970-01-01
    • 2016-12-14
    • 2021-01-14
    • 1970-01-01
    • 2017-01-06
    • 1970-01-01
    相关资源
    最近更新 更多