在Hibernate-Envers中,如何使实体的较早修订成为最新修订

时间:2018-09-05 12:26:54

标签: java hibernate hibernate-envers

我的应用程序使用Hibernate-Envers 4.3.11

  • 我将音乐文件及其元数据(例如专辑标题,艺术家)以经过审核的 Song
  • 的形式加载到数据库中
  • 然后应用程序以各种方式编辑元数据,并写回Song类(每次提交的会话都会创建另一个修订版本)
  • 然后通常在最后将更改从当前Song类写回到文件本身。
  • 但是如果以预览模式运行,则不会将任何内容写入文件。因此,我希望该文件的Song类的最新版本现在包含与最初加载文件时创建的版本相同的元数据。

当前,我通过获取Song的第一个修订版并将其保存的数据复制到Song的最新版本中来进行此操作。 但是我只能说采用此较早的修订并使其成为最新的修订

更新

我现在正在执行Naros的答案。因此,现在我有了一个名为 SongFile 的类,该类始终表示磁盘上音乐文件的内容,这是@Audited(由Envers提供),主要ID是@GeneratedValue。

然后,我们创建了一个名为 Song 的类,在创建了 SongFile 实体并将其省略后,我们创建了一个等效的 Song 实体。这未经审核,并且手动设置了主要ID,我们将primaryId和所有元数据设置为与Song类相同。

然后,该应用将元数据修改为 Song

然后得出结论,如果处于 Preview模式,我们只需比较 Song SongFile 之间的差异并生成报告即可。

如果处于真实保存模式,我们将比较 Song SongFile 之间的差异,并生成报告并将更改保存回文件中,然后 SongFile

这很好用,并且解决了许多问题。

但是,如果我遇到问题,音乐文件也可以存储多个图像(jpeg等),我们使用表示图像的 CoverImage 类和用于表示图像的 CoverArt 类对此进行建模提供 Song CoverImage 之间的1:M链接,还存储了name属性。

现在的问题是,我创建了一个 SongFile 使用的 CoverArtFile 类,该类使用@GeneratedValue和一个使用的 CoverArt 类不会自动生成的歌曲

最初加载文件并创建 SongFile Song 时,此方法可以正常工作,将自动生成的值从任何CoverArtFile类复制到 CoverArt < / em>类

但是,如果我们没有Coverart开头,然后又被应用程序添加,则会失败,因为 CoverArt 类不会自动生成主键,如果使用它,我将无法安全地生成一个主键由现有的 CoverArtFile 类(或将来的版本)提供。

如果我有一个使用自动生成的值的CoverArt,那么它将不会与其关联的CoverArtFile类所使用的值相同。

我该如何进行?

1 个答案:

答案 0 :(得分:1)

首先,没有自动方法可以完成您所要求的操作。该过程必须完全手动进行并在应用程序中进行编码,因为存在许多实体差异和业务注意事项,最好由应用程序来决定和处理。

所以最好的方法是

  1. 从要还原到的审核历史记录中获取所需的修订。
  2. 从Hibernate获取现有实体。
  3. 在(2)中设置现有实体中的值,并在(1)中获取所需修订版本中的值。
  4. 合并修改后的实体。

另一个可行的想法是考虑将预览非预览的概念分开。我的意思是您有两个实体,而不是一个Song。您有一个代表 preview 的高度易失版本,另一个代表不易波动的 non-preview ,其意为表示文件内容。

在这种方法中,当您修改 preview 变体时,您只是使用Envers创建了一个更改日志,以查找可能是由于该文件而并非由于 preview-模式。修改非预览版变体时,您正在为文件内容创建一个更改日志。

需要注意的是,在这种用例中,您的业务逻辑可能还应该同步 preview 变体,以使 preview non-preview em>具有相同的内容,因此基本上加载 preview 实体,对其进行修改以匹配 non-preview ,然后也保存对其所做的更改。

如果两种实体类型都具有通用的主键或自然ID,则这应该易于管理,并允许您生成所有类型的报告并保持一致的历史记录,而无需担心。

更新

添加到我关于您的更新的答案中;在这里,您可能希望对所有实体使用生成的标识符,并使SongCoverArt实体维护对已审计实体的引用,或者至少存储主键值。

例如

@Entity
public class Song {
  @Id
  @GeneratedValue
  private Long id;
  @OneToOne
  private SongFile songFile;      
}

或更简单地

@Entity
public class Song {
  @Id
  @GeneratedValue
  private Long id;
  private Long songFileId;
}

这个想法是,您的非基于文件的实体可以通过关联(例如@OneToOne)或存储关联实体的标识符来保持与文件端的关系,以便您可以确定是否需要获取/复制或构造/插入。