My application uses Hibernate-Envers 4.3.11
Currently I do this by getting the first revision of Song and copying the data it holds to the latest version of Song. but is there a way I can just say take this earlier revision and make it the latest revision.
Update
I am now implementing the answer of Naros. So now I have a class called SongFile that always represents the contents of the music file on disk, this is @Audited (by Envers) and the primary id is a @GeneratedValue.
Then we have a class called Song, after an SongFile entity is created and omitted we create an equivalent Song entity. This is not audited and the primary id is manually set, we set the primaryId and all the metadata so the same as the Song class.
The app then modifies the metadata to Song.
Then at conclusion if in Preview mode, we just compare differences between Song and SongFile and generate report.
If in Real save mode, we compare differences between Song and SongFile and generate report and save changes back to file, and SongFile.
This is working great and solves a number of problems.
However I have an issue, music files can also store multiple images (jpegs etc) we model this with a CoverImage class representing the image, and a CoverArt class that provides a 1:M link between Song and CoverImage, also stores a name attribute.
Now the problem is that I created a CoverArtFile class used by SongFile that uses @GeneratedValue and a CoverArt class used by Song that doesn't autogen.
When originally loading a file and creating SongFile and Song this works fine, copying over the autogenned value from any CoverArtFile
classes to CoverArt class
However if we have no coverart to start with and then it is added by the app we fail because the CoverArt class doesn't autogen primary key and I cannot safetly generate one in case it is used by an existing CoverArtFile class( or will be in future).
If alternatively I have CoverArt using an autogened value then it wont be the same value as used by its associated CoverArtFile class.
How do I proceed ?
First there is no automated way to do what you request. This process has to be completely manual and coded in the application as there are many entity nuances and business considerations that are best left to the application to decide and handle.
So the best way to do this would be
Another idea that might work would be to consider separating the concept of preview and non-preview. What I mean is you have two entities rather than a single Song
. You have one that represents the highly voltile version for preview and another for the less voltile non-preview that is meant to represent the file contents.
In this approach when you modify the preview variant, you're just creating a change log with Envers for things that could have been made to the file but were not due to preview-mode. When you modify the non-preview variant, you're creating a change log for what the file contents should be.
The caveat here is your business logic should probably also synchronize the preview variant in this use case so that both the preview and the non-preview have the same contents, so basically load the preview entity, modify it to match the non-preview and then save the changes for it too.
If both entity types have a common primary key or natural-id, this should be easy enough to manage and allow you to generate all types of reports and maintain consistent history without mixing concerns.
To add to my answer regarding your update; this is where you probably will want to use generated identifiers for all entities and have your Song
and CoverArt
entities maintain a refererence to either the audited entity or at the very least store the primary key value.
As an example
@Entity
public class Song {
@Id
@GeneratedValue
private Long id;
@OneToOne
private SongFile songFile;
}
or more simply
@Entity
public class Song {
@Id
@GeneratedValue
private Long id;
private Long songFileId;
}
The idea is that your non-file based entities maintain a relationship to the file side either by an association (e.g. @OneToOne
) or store the identifier of the associated entity so that you can determine if you need to fetch/replicate or construct/insert.