【问题标题】:HIbernate Envers: foreign key to revisionHIbernate Envers:修改的外键
【发布时间】:2019-03-01 08:01:36
【问题描述】:

是否可以在实体的修订中添加外键?

例如,如果我有实体Person

@Entity
@Audited
public class Person {
    @Id
    @Column
    private Integer idPerson;
    @Column
    private String favoriteHobby;
}

我想指出一个人的修订。假设我创建了一本年鉴,我希望它包含那个人目前最喜欢的爱好,但如果这个人之后会更新他/她最喜欢的爱好,我希望年鉴包含旧的爱好。

在 SQL 中,这将通过创建一个包含 idPersonidRevisionidYearBook 列的表来完成。

但我不确定是否以及如何将其转换为实体、查询它、创建我的年鉴...而不编写自定义查询或复制我的个人实体。

【问题讨论】:

  • YearBook 在您的数据模型中被认为是不可变的吗?如果是这样,如果你能确定你所追求的特定年鉴,你就可以遍历所有关联的Person 并获取你寻找的信息。
  • @Naros 您的意思是,如果我同时审核 PersonYearBook,我将能够获得存档的年鉴并遍历当时与该年鉴相关联的所有人员在他们目前的状态?

标签: hibernate hibernate-envers


【解决方案1】:

首先回答 cmets 的问题,是的。

如果您将两个实体都标记为 @Audited,则您对任一实体所做的任何更改都会导致在该特定实体的审计架构中创建一个新的审计行。

AuditReader API 模仿了许多旧的传统 Hibernate Criteria API。您基本上可以根据已审核的实体类型构建任何查询,并应用大量条件来过滤结果。

这里我想提一个警告。

假设在一个事务中,我们创建了一个Person 并将它们放入YearBook

final Session session = openSession();
try {
   session.getTransaction().begin();

   final Person person = new Person();
   person.setName( "John Doe" );
   person.setHobby( "Plays Guitar" );
   session.save( person );

   final YearBook yearBook = new YearBook();
   yearBook.setYear( 2018 );
   yearBook.getPeople().add( person );
   session.save( yearBook );

   session.getTransaction().commit();
}
catch( Exception ex ) {
   if ( session.getTransaction().isActive() ) {
     session.getTransaction().rollback();
   }
   throw ex;
}
finally {
  session.close();
}

这意味着在审计架构中,将为这两种实体类型创建一个新的审计行,并且这些行将与相同的修订号相关联。为简单起见,假设是修订号 1

在未来的交易中,您为此John Doe修改Person

final Session session = openSession();
try {
  session.getTransaction().begin();

  final Person person = session.find( Person.class, personId );
  person.setHobby( "Video Games" );

  session.getTransaction().commit();
}
catch( Exception ex ) {
   if ( session.getTransaction().isActive() ) {
     session.getTransaction().rollback();
   }
   throw ex;
}
finally {
  session.close();
}

此时:

  • YearBook 作为单个修订版,修订版 #1
  • John DoePerson 有两个修订版,修订版 #1 和 #2

这里需要注意的是,针对修订版 1YearBook 查询将永远不会在修订版 2 中返回有关 Person 的任何信息。这绝对是几乎所有情况下的预期结果;但是在某些特殊情况下,用户也希望 YearBook 触发修订。

如果您希望集合所有者在修改不影响集合本身状态的元素属性时也生成修订,您需要在容器上放置一个虚拟属性并将其作为一部分进行修改交易,以便双方都得到审计。

也就是说,您将如何获得 YearBook 的修订版?使用AuditReader API 非常简单:

final AuditReader reader = AuditReaderFactory.get( session );

// Get the revisions in ascending order from smallest to highest rev #
List<Number> revisions = reader
  .forRevisionsOfEntity( YearBook.class, false )
  .getResultList();

如果您已经知道感兴趣的修订号,则可以使用以下方法获取该实体的确切快照:

final AuditReader reader = AuditReaderFactory.get( session );
final YearBook yearBook = reader.find( YearBook.class, yearBookId, revision );

查看文档,了解使用 Envers 提供的 AuditReader 接口创建查询的所有其他方法。

【讨论】:

  • 感谢您的详尽解释!我当前的解决方案是复制我的 Person 实体(如拍摄快照)并指向该快照。它不像您的解决方案那么漂亮,但它让我可以更好地控制何时制作快照。我还想知道如果您在编辑Person 后更新YearBook 会发生什么。我猜 yearBook 将在修订版 #3 上。但它还会指向修订#1 中的人吗?
  • 啊等一下。这就是你问我YearBook 是否不可变的原因?从文档中不是很清楚,我应该更详细地测试它,但我猜#3 上的 yearBook 将指向 #3 上的链接人员(或者如果更新没有级联,则可能是 #2)。就我而言,YearBook 不是一成不变的,所以我必须坚持自己的解决方案。有点难过,因为我现在有非常相似的重复实体。 (我尝试使用继承,但这会导致我的元模型生成出现问题,但这是另一个问题。)
  • 没错。每当您修改 YearBook 时,它都被视为对象模型的新“快照”,因此此时您对 Person 实体所做的任何更改自 YearBook 的先前修订版以来现在都将包含在您获取修订版时正在生成。换句话说,它创建了实体对象模型的快照,而不仅仅是特定实体。
  • 我相信这里最简洁的方法可能是使用多个实体模型来表示您想要的数据。例如,您可以审核 YearBookPerson 以捕获对它们所做的所有更改。然后,您可以拥有另外两种实体类型,YearBook2Person2,您可以在其中复制基于某些业务规则的更改并对其进行审核。在一个模型中跟踪每个更改并在另一个模型中根据某些业务规则跟踪快照,从而获得两全其美的结果。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-03-31
  • 2015-09-20
  • 2019-08-20
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多