Hibernate Envers 'forEntitiesAtRevision' генерирует избыточный подзапрос - PullRequest
0 голосов
/ 26 мая 2020

Я использую Hibernate Envers для аудита изменений сущностей. Сущности и таблицы аудита хранятся в базе данных MySQL. Решение работает нормально в большинстве случаев, но я обнаружил довольно странную проблему с этим.

Допустим, у меня есть состояние объекта в определенной версии, и я хочу получить предыдущее изменение для этого объекта. Для этого я написал следующий метод:

    public <T> T getLastChange(@Nonnull Object id, @Nonnull Class<T> type, long beforeRev) {
        List<Number> revisions = AuditReaderFactory.get(entityManager).getRevisions(type, id);

        return revisions.stream()
                .map(Number::longValue)
                .filter(rev -> rev < beforeRev)
                .max(Comparator.comparingLong(rev -> rev))
                .map(rev -> AuditReaderFactory.get(entityManager).find(type, type.getName(), id, rev, true))
                .orElse(null);
    }

Он отлично работает, за исключением того, что find генерирует «слишком безопасный» запрос:

select *
from persons_AUD person_aud0_
where person_aud0_.REV=(select max(person_aud1_.REV) from persons_AUD person_aud1_ where person_aud1_.REV<=462864 and person_aud0_.id=person_aud1_.id)
  and person_aud0_.id=56591;

I уже знаю точную ревизию! Как избежать этого подзапроса ?? Я просто хочу

select *
from persons_AUD person_aud0_
where person_aud0_.REV=462864
  and person_aud0_.id=56591;

В настоящее время это вызывает серьезные проблемы с производительностью для больших таблиц.

Буду признателен за любой совет о том, как я могу убедить Envers использовать переданную ревизию как точное совпадение.

1 Ответ

0 голосов
/ 29 мая 2020

Я нашел решение своей проблемы. AuditReader.find метод использует forEntitiesAtRevision, который всегда производит этот подзапрос. Но после некоторых экспериментов я заметил, что другой метод forRevisionsOfEntity может делать именно то, что я хочу. Поэтому я переписал свой запрос следующим образом:

    public <T> T getPreviousChange(@Nonnull Object id, @Nonnull Class<T> type, int beforeRev) {

        List resultList = getEntityManager().createQuery()
                .forRevisionsOfEntity(type, type.getName(), true, true)
                .add(AuditEntity.id().eq(id))
                .add(AuditEntity.revisionNumber().lt(beforeRev))
                .addOrder(AuditEntity.revisionNumber().desc())
                .setMaxResults(1).getResultList();

        return resultList.isEmpty() ? null : (T) resultList.get(0);
    }

И теперь вместо двух запросов я получаю один:

select *
from persons_AUD person_aud0_ 
where person_aud0_.id=? and person_aud0_.REV<? 
order by person_aud0_.REV desc limit ?

, который неплохо работает даже на большой таблице.

PS Конструктор AuditQuery на самом деле довольно богатый и мощный! Запрос легко изменить нужным образом. Спасибо.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...