Получить предыдущую версию объекта в Hibernate Envers

У меня есть объект, загруженный Hibernate (через EntityManager):

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

Этот класс проверяется с помощью Hibernate Envers. Как загрузить предыдущую версию объекта User?

Ответ 1

возможно, это тогда (из 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));

(Я очень новичок в этом, не уверен, что у меня есть весь синтаксис, возможно, size() - 1 должен быть size() - 2?)

Ответ 2

Здесь другая версия, которая находит предыдущую ревизию относительно "текущего" номера версии, поэтому ее можно использовать, даже если сущность, на которую вы смотрите, не является последней версией. Он также обрабатывает случай, когда нет предварительной ревизии. (em считается ранее заполненным 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
}

Это можно обобщить на:

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
}

Единственный сложный бит с этим обобщением - это получение идентификатора объекта. Потому что я использую Play! framework, я могу использовать тот факт, что все сущности являются Моделями и используют ((Model) entity).id для получения идентификатора, но вам придется настроить его в соответствии с вашей средой.

Ответ 3

Из документов:

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

Ответ 4

Я думаю, это будет так:

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

Ответ 5

Исходя из превосходного подхода @brad-mace, я внес следующие изменения:

  • Вы должны передать в свой EntityClass и Id вместо hardcoding и принять модель.
  • Не переустанавливайте свой EntityManager.
  • Нет настройки точки selectDeleted, потому что удаленная запись никогда не может быть возвращена в качестве предыдущей версии.
  • Вызов получения одного результата с помощью throw и exception, если не найдено ни одного результата или более одного результата, поэтому либо вызывайте результирующий список, либо улавливайте исключение (это решение вызывает getResultList с maxResults = 1)
  • Получить ревизию, тип и сущность в одной транзакции (удалить проекцию, использовать orderBy и maxResults и запрос для объекта [3])

Итак, другое решение:

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