Reputation: 697
Is it possible to add a foreign key to a revision of an entity?
For example if I have the entity Person
:
@Entity
@Audited
public class Person {
@Id
@Column
private Integer idPerson;
@Column
private String favoriteHobby;
}
I would like to point to a revision of a person. Let's say I create a yearbook and I would like it to contain the favorite hobby of that person at the moment, but if the person would update his/her favorite hobby afterwards, I want the yearbook to contain the old hobby.
In SQL, this would be accomplished by creating a table with the columns idPerson
, idRevision
and idYearBook
.
But I'm not sure if and how I can translate this into an entity, query it, create my yearbook... without writing custom queries or duplication my person entity.
Upvotes: 0
Views: 1918
Reputation: 21103
To answer your question from the comments first, Yes.
If you marked both entities as @Audited
, any change you make to either entity would result in a new audit row being created in the audit schema for that specific entity.
The AuditReader
API mimics much of the old legacy Hibernate Criteria API. You basically can build any query based on an audited entity type and apply a plethora of conditions on which to filter the results.
There is one caveat here I want to mention.
Lets assume in a transaction, we create a Person
and put them in the 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();
}
This would mean that in the audit schema, a new audit row would be created for both of these entity types and those rows would be associated to the same revision number. For simplicity sake, lets say that is revision number 1.
In a future transaction, you modify Person
for this John Doe:
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();
}
At this point:
YearBook
as a single revision, revision #1.Person
for John Doe has two revisions, revisions #1 and #2.The caveat here is that queries against YearBook
for revision 1 will never return you anything about Person
in revision 2. This is definitely the desired result in almost all cases; however there are some unique situations where users want YearBook
to trigger a revision too.
If you want the collection owner to also generate a revision when an element attribute is modified that doesn't impact the state of the collection itself, you'll need to place a dummy attribute on the container and modify it as a part of the transaction so both get audited.
That said, how would you go about getting YearBook
at its revisions? That's pretty straight forward using the 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();
If you already know the revision number that you're interested in, you can then get the exact snapshot of that entity using:
final AuditReader reader = AuditReaderFactory.get( session );
final YearBook yearBook = reader.find( YearBook.class, yearBookId, revision );
Check out the documentation for all the other ways to create queries using the AuditReader
interface provided by Envers.
Upvotes: 3