Reputation: 4103
I want to get my Envers to return the entities with the highest revision that is still below a given revision number.
What I came up with is the following:
@PersistenceContext
private EntityManager em;
@Test
public void test() throws Exception {
final Number before = AuditReaderFactory.get(this.em).getRevisionNumberForDate(new Date(Long.MAX_VALUE));
final UserEntity user = new UserEntity().loginName("one");
doInTransaction(() -> this.em.persist(user));
final Number middle = AuditReaderFactory.get(this.em).getRevisionNumberForDate(new Date(Long.MAX_VALUE));
user.setLoginName("two");
doInTransaction(() -> this.em.persist(user));
final Number after = AuditReaderFactory.get(this.em).getRevisionNumberForDate(new Date(Long.MAX_VALUE));
Assert.assertEquals(new ArrayList<>(), getRevisions(before));
Assert.assertEquals(Arrays.asList(user), getRevisions(middle));
Assert.assertEquals(new ArrayList<>(), getRevisions(after));
}
private void doInTransaction(Runnable magic) {
try {
this.em.getTransaction().begin();
magic.run();
this.em.getTransaction().commit();
} catch (final Exception e) {
this.em.getTransaction().rollback();
throw new RuntimeException(e);
}
}
private List<UserEntity> getRevisions(Number revisionNumber) {
final AuditReader auditReader = AuditReaderFactory.get(this.em);
try {
return auditReader.createQuery().forRevisionsOfEntity(UserEntity.class, true, false)
.add(AuditEntity.revisionNumber().maximize().computeAggregationInInstanceContext()) // the highest revision possible
.add(AuditEntity.revisionNumber().le(revisionNumber)) // as long as the revision is below or equal to revisionNumber
.getResultList();
} catch (final NoResultException e) {
return null;
}
}
The method that does not work is getRevisions(Object, Number)
. It returns nothing for the before
revision (that's correct), it returns still nothing for the middle
revision (not correct, now the entity exists!) and then returns something for the after
revision (also not correct, the entity was deleted).
The reason for that behavior is clear when we look at the resulting SQL:
SELECT * from user_aud u
WHERE u.REV = (
SELECT MAX (v.REV)
FROM user_aud v
WHERE v.id=u.id
)
AND u.REV <= ? ORDER BY u.REV asc
The u.REV <= ?
part should be inside the inner query. The way it's generated now Hibernate decides that the max revision is 3, and then selects nothing because rev should be below 2.
I can't seem to figure out how to fix the query, and there is very little documentation on the subject. I tried removing computeAggregationInInstanceCont()
, but there was no change in how the code behaved.
What is the correct query to get the highest revision below a user-defined value?
Upvotes: 0
Views: 1109
Reputation: 4103
This problem boiled down to setting the brackets correctly:
private List<UserEntity> getRevisions(Number revisionNumber) {
final AuditReader auditReader = AuditReaderFactory.get(this.em);
try {
return auditReader.createQuery().forRevisionsOfEntity(UserEntity.class, true, false)
.add(AuditEntity.revisionNumber().maximize().computeAggregationInInstanceContext().add(AuditEntity.revisionNumber().le(revisionNumber)))
.getResultList();
} catch (final NoResultException e) {
return null;
}
}
Upvotes: 1
Reputation: 21123
You should be able to acquire the highest revision number below a specified value as follows:
AuditReaderFactory.get( session )
.createQuery()
.forRevisionsOfEntity( EntityClass.class, true, true )
.addOrder( AuditEntity.revisionNumber().desc() )
.add( AuditEntity.id().eq( entityId ) )
.add( AuditEntity.revisionNumber().le( userSuppliedValue ) )
.setMaxResults( 1 )
.singleResult();
This will order all revisions in descending order and returns the first entry. The rows in the result set will be for EntityClass
entity with primary key entityId
and applies the restriction that the the entities had to be audited with a revision number less-than or equal-to the supplied value.
Upvotes: 1