在 Hibernate-Envers 中如何使实体的早期修订成为最新修订
In Hibernate-Envers how to make earlier revison of an entity the latest revision
我的应用程序使用 Hibernate-Envers 4.3.11
- 我将音乐文件加载到我的数据库中,并将其元数据(例如专辑名称、艺术家)表示为经过审核的 歌曲 class
- 然后应用程序以各种方式编辑元数据,并写回歌曲class(每次提交的会话都会创建另一个修订)
- 然后通常在最后会将当前歌曲 class 中的更改写回文件本身。
- 但如果 运行 在 预览模式 中,则不会向文件写入任何内容。所以我希望该文件的歌曲 class 的最新版本现在包含与最初加载文件时创建的版本相同的元数据。
目前我通过获取 Song 的第一个修订版并将其保存的数据复制到最新版本的 Song 来实现这一点。 但是有没有办法让我可以直接说采用这个较早的修订版并将其设为最新修订版。
更新
我现在正在执行Naros的答案。所以现在我有一个名为 SongFile 的 class,它始终代表磁盘上音乐文件的内容,这是 @Audited(由 Envers 提供),主要 ID 是 @GeneratedValue .
然后我们有一个名为 Song 的 class,在创建并省略 SongFile 实体后,我们创建一个等价的 歌曲实体。这是未经审计的,主要 ID 是手动设置的,我们将 primaryId 和所有元数据设置为与歌曲相同 class。
应用随后将元数据修改为歌曲。
那么最后如果在预览模式下,我们只是比较Song和SongFile[=76=之间的差异] 并生成报告。
如果在Real save模式下,我们比较Song和SongFile之间的差异并生成报告并将更改保存回文件,以及 SongFile.
效果很好并解决了很多问题。
但是我有一个问题,音乐文件也可以存储多个图像(jpeg 等)我们用 CoverImage class 表示图像,以及 CoverArt class 在 Song 和 CoverImage[=76 之间提供 1:M link =], 还存储了一个名称属性。
现在的问题是我创建了一个 CoverArtFile class 供 SongFile 使用,它使用 @GeneratedValue 和 CoverArt class 被 Song 使用,不自动生成。
当最初加载文件并创建 SongFile 和 Song 时,这工作正常,从任何 CoverArtFile
class转 封面 class
但是,如果我们没有开始的封面艺术,然后由应用程序添加它,我们就会失败,因为 CoverArt class 不会自动生成主键,我无法安全地生成一个,以防它被现有的 CoverArtFile class(或将来使用)使用。
如果我有 CoverArt 使用自动生成的值,那么它不会与其关联的 CoverArtFile 使用的值相同 class。
我该如何进行?
首先,没有自动方法来执行您的请求。此过程必须完全手动并在应用程序中编码,因为有许多实体细微差别和业务注意事项最好留给应用程序来决定和处理。
所以最好的方法是
- 从要还原到的审核历史记录中获取所需的修订。
- 从 Hibernate 中获取现有实体。
- 将来自 (2) 的现有实体中的值设置为来自 (1) 中获取的所需修订的值。
- 合并修改后的实体。
另一个可能可行的想法是考虑将 preview 和 non-preview 的概念分开。我的意思是你有两个实体而不是一个 Song
。您有一个代表 preview 的高度易变版本,另一个代表 non-preview 的不太易变的版本,它代表文件内容。
在这种方法中,当您修改 preview 变体时,您只是使用 Envers 创建一个更改日志,用于记录本可以对文件进行但不是由于预览模式。当您修改 非预览版 变体时,您正在为文件内容 应该 创建一个更改日志。
这里需要注意的是,您的业务逻辑可能还应该同步此用例中的 preview 变体,以便 preview 和非预览内容相同,所以基本上加载预览实体,修改它以匹配非预览 然后也保存它的更改。
如果两种实体类型都有共同的主键或自然 ID,这应该很容易管理,并允许您生成所有类型的报告并保持一致的历史记录,而不会混淆问题。
更新
添加到我对您的更新的回答中;这是您可能希望为所有实体使用生成的标识符并让您的 Song
和 CoverArt
实体维护对审计实体的引用或至少存储主键值的地方。
举个例子
@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
)或存储关联实体的标识符来维护与文件端的关系,以便您可以确定是否需要至 fetch/replicate 或 construct/insert.
我的应用程序使用 Hibernate-Envers 4.3.11
- 我将音乐文件加载到我的数据库中,并将其元数据(例如专辑名称、艺术家)表示为经过审核的 歌曲 class
- 然后应用程序以各种方式编辑元数据,并写回歌曲class(每次提交的会话都会创建另一个修订)
- 然后通常在最后会将当前歌曲 class 中的更改写回文件本身。
- 但如果 运行 在 预览模式 中,则不会向文件写入任何内容。所以我希望该文件的歌曲 class 的最新版本现在包含与最初加载文件时创建的版本相同的元数据。
目前我通过获取 Song 的第一个修订版并将其保存的数据复制到最新版本的 Song 来实现这一点。 但是有没有办法让我可以直接说采用这个较早的修订版并将其设为最新修订版。
更新
我现在正在执行Naros的答案。所以现在我有一个名为 SongFile 的 class,它始终代表磁盘上音乐文件的内容,这是 @Audited(由 Envers 提供),主要 ID 是 @GeneratedValue .
然后我们有一个名为 Song 的 class,在创建并省略 SongFile 实体后,我们创建一个等价的 歌曲实体。这是未经审计的,主要 ID 是手动设置的,我们将 primaryId 和所有元数据设置为与歌曲相同 class。
应用随后将元数据修改为歌曲。
那么最后如果在预览模式下,我们只是比较Song和SongFile[=76=之间的差异] 并生成报告。
如果在Real save模式下,我们比较Song和SongFile之间的差异并生成报告并将更改保存回文件,以及 SongFile.
效果很好并解决了很多问题。
但是我有一个问题,音乐文件也可以存储多个图像(jpeg 等)我们用 CoverImage class 表示图像,以及 CoverArt class 在 Song 和 CoverImage[=76 之间提供 1:M link =], 还存储了一个名称属性。
现在的问题是我创建了一个 CoverArtFile class 供 SongFile 使用,它使用 @GeneratedValue 和 CoverArt class 被 Song 使用,不自动生成。
当最初加载文件并创建 SongFile 和 Song 时,这工作正常,从任何 CoverArtFile
class转 封面 class
但是,如果我们没有开始的封面艺术,然后由应用程序添加它,我们就会失败,因为 CoverArt class 不会自动生成主键,我无法安全地生成一个,以防它被现有的 CoverArtFile class(或将来使用)使用。
如果我有 CoverArt 使用自动生成的值,那么它不会与其关联的 CoverArtFile 使用的值相同 class。
我该如何进行?
首先,没有自动方法来执行您的请求。此过程必须完全手动并在应用程序中编码,因为有许多实体细微差别和业务注意事项最好留给应用程序来决定和处理。
所以最好的方法是
- 从要还原到的审核历史记录中获取所需的修订。
- 从 Hibernate 中获取现有实体。
- 将来自 (2) 的现有实体中的值设置为来自 (1) 中获取的所需修订的值。
- 合并修改后的实体。
另一个可能可行的想法是考虑将 preview 和 non-preview 的概念分开。我的意思是你有两个实体而不是一个 Song
。您有一个代表 preview 的高度易变版本,另一个代表 non-preview 的不太易变的版本,它代表文件内容。
在这种方法中,当您修改 preview 变体时,您只是使用 Envers 创建一个更改日志,用于记录本可以对文件进行但不是由于预览模式。当您修改 非预览版 变体时,您正在为文件内容 应该 创建一个更改日志。
这里需要注意的是,您的业务逻辑可能还应该同步此用例中的 preview 变体,以便 preview 和非预览内容相同,所以基本上加载预览实体,修改它以匹配非预览 然后也保存它的更改。
如果两种实体类型都有共同的主键或自然 ID,这应该很容易管理,并允许您生成所有类型的报告并保持一致的历史记录,而不会混淆问题。
更新
添加到我对您的更新的回答中;这是您可能希望为所有实体使用生成的标识符并让您的 Song
和 CoverArt
实体维护对审计实体的引用或至少存储主键值的地方。
举个例子
@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
)或存储关联实体的标识符来维护与文件端的关系,以便您可以确定是否需要至 fetch/replicate 或 construct/insert.