简体   繁体   中英

Get previous version of entity in Hibernate Envers

I have an entity loaded by Hibernate (via EntityManager ):

User u = em.load(User.class, id)

This class is audited by Hibernate Envers. How can I load the previous version of a User entity?

Here's another version that finds the previous revision relative to a "current" revision number, so it can be used even if the entity you're looking at isn't the latest revision. It also handles the case where there isn't a prior revision. ( em is assumed to be a previously-populated EntityManager)

public static User getPreviousVersion(User user, int current_rev) {
    AuditReader reader = AuditReaderFactory.get(em);

    Number prior_revision = (Number) reader.createQuery()
    .forRevisionsOfEntity(User.class, false, true)
    .addProjection(AuditEntity.revisionNumber().max())
    .add(AuditEntity.id().eq(user.getId()))
    .add(AuditEntity.revisionNumber().lt(current_rev))
    .getSingleResult();

    if (prior_revision != null)
        return (User) reader.find(User.class, user.getId(), prior_revision);
    else
        return null
}

This can be generalized to:

public static T getPreviousVersion(T entity, int current_rev) {
    AuditReader reader = AuditReaderFactory.get(JPA.em());

    Number prior_revision = (Number) reader.createQuery()
    .forRevisionsOfEntity(entity.getClass(), false, true)
    .addProjection(AuditEntity.revisionNumber().max())
    .add(AuditEntity.id().eq(((Model) entity).id))
    .add(AuditEntity.revisionNumber().lt(current_rev))
    .getSingleResult();

    if (prior_revision != null)
        return (T) reader.find(entity.getClass(), ((Model) entity).id, prior_revision);
    else
        return null
}

The only tricky bit with this generalization is getting the entity's id. Because I'm using the Play! framework, I can exploit the fact that all entities are Models and use ((Model) entity).id to get the id, but you'll have to adjust this to suit your environment.

maybe this then (from AuditReader docs)

AuditReader reader = AuditReaderFactory.get(entityManager);
User user_rev1 = reader.find(User.class, user.getId(), 1);

List<Number> revNumbers = reader.getRevisions(User.class, user_rev1);
User user_previous = reader.find(User.class, user_rev1.getId(),
  revNumbers.get(revNumbers.size()-1));

(I'm very new to this, not sure if I have all the syntax right, maybe the size()-1 should be size()-2?)

I think it would be this:

final AuditReader reader = AuditReaderFactory.get( entityManagerOrSession );

// This could probably be declared as Long instead of Object
final Object pk = userCurrent.getId();

final List<Number> userRevisions = reader.getRevisions( User.class, pk );

final int revisionCount = userRevision.size();

final Number previousRevision = userRevisions.get( revisionCount - 2 );

final User userPrevious = reader.find( User.class, pk, previousRevision );

Building off of the excellent approach of @brad-mace, I have made the following changes:

  • You should pass in your EntityClass and Id instead of hardcoding and assuming the Model.
  • Don't hardcode your EntityManager.
  • There is no point setting selectDeleted, because a deleted record can never be returned as the previous revision.
  • Calling get single result with throw and exception if no results or more than 1 result is found, so either call resultlist or catch the exception (this solution calls getResultList with maxResults = 1)
  • Get the revision, type, and entity in one transaction (remove the projection, use orderBy and maxResults, and query for the Object[3] )

So here's another solution:

public static <T> T getPreviousRevision(EntityManager entityManager, Class<T> entityClass, Object entityId, int currentRev) {
    AuditReader reader = AuditReaderFactory.get(entityManager);
    List<Object[]> priorRevisions = (List<Object[]>) reader.createQuery()
            .forRevisionsOfEntity(entityClass, false, false)
            .add(AuditEntity.id().eq(entityId))
            .add(AuditEntity.revisionNumber().lt(currentRev))
            .addOrder(AuditEntity.revisionNumber().desc())
            .setMaxResults(1)
            .getResultList();

    if (priorRevisions.size() == 0) {
        return null;
    }
    // The list contains a single Object[] with entity, revinfo, and type 
    return (T) priorRevision.get(0)[0];
}

From the docs:

AuditReader reader = AuditReaderFactory.get(entityManager);
User user_rev1 = reader.find(User.class, user.getId(), 1);

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