简体   繁体   中英

Return a Specific Revision of An Object

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?

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.

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;
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM