Ручная индексация поиска Hibernate выдает «org.hibernate.TransientObjectException: экземпляр не был связан с этим сеансом» - PullRequest
0 голосов
/ 06 июля 2019

Я использую Hibernate Search 5.11 в своем приложении Spring Boot 2, что позволяет проводить полнотекстовые исследования.Это библиотека требует индексации документов.

Когда мое приложение запускается, я пытаюсь вручную переиндексировать данные индексируемой сущности (MyEntity.class) каждые пять минут (по конкретной причине, из-за контекста моего сервера).

Я пытаюсь проиндексировать данные MyEntity.class.

MyEntity.class имеет свойство attachFiles, которое представляет собой хэш-набор, заполненный объединением @OneToMany (), с режимом отложенной загрузкиenabled:

@OneToMany(mappedBy = "myEntity", cascade = CascadeType.ALL, orphanRemoval = true)
private Set<AttachedFile> attachedFiles = new HashSet<>();

Я кодирую требуемый процесс индексации, но возникает исключение для " fullTextSession.index (result); ", когда свойство attachFiles данного объекта заполнено однимили более элементов:

org.hibernate.TransientObjectException: The instance was not associated with this session

Режим отладки указывает на сообщение типа «Невозможно загрузить [...]» для значения хэш-значения сущности в этом случае.

И если HashSet пуст(не ноль, только пусто), исключение не выдается.

Мой метод индексации:

private void indexDocumentsByEntityIds(List<Long> ids) {

final int BATCH_SIZE = 128;

Session session = entityManager.unwrap(Session.class);

FullTextSession fullTextSession = Search.getFullTextSession(session);
fullTextSession.setFlushMode(FlushMode.MANUAL);
fullTextSession.setCacheMode(CacheMode.IGNORE);

CriteriaBuilder builder = session.getCriteriaBuilder();
CriteriaQuery<MyEntity> criteria = builder.createQuery(MyEntity.class);
Root<MyEntity> root = criteria.from(MyEntity.class);
criteria.select(root).where(root.get("id").in(ids));

TypedQuery<MyEntity> query = fullTextSession.createQuery(criteria);

List<MyEntity> results = query.getResultList();

int index = 0;

for (MyEntity result : results) {
    index++;
    try {
        fullTextSession.index(result); //index each element
        if (index % BATCH_SIZE == 0 || index == ids.size()) {
            fullTextSession.flushToIndexes(); //apply changes to indexes
            fullTextSession.clear(); //free memory since the queue is processed
        }
    } catch (TransientObjectException toEx) {
        LOGGER.info(toEx.getMessage());
        throw toEx;
    }
}
}

У кого-нибудь есть идея?

Спасибо!

Ответы [ 2 ]

1 голос
/ 08 июля 2019

Это, вероятно, вызвано "чистым" вызовом, который у вас есть в вашем цикле.

По сути, то, что вы делаете:

  • загрузка всех сущностей для переиндексации в сеанс
  • индексировать одну партию объектов
  • удалить все сущности из сеанса (fullTextSession.clear())
  • попытаться проиндексировать следующий пакет сущностей, даже если они больше не находятся в сеансе ...?

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

В документации приведен пример того, как это сделать, используя прокрутку и соответствующий размер пакета: https://docs.jboss.org/hibernate/search/5.11/reference/en-US/html_single/#search-batchindex-flushtoindexes

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

0 голосов
/ 10 июля 2019

Спасибо за объяснения @yrodiere, они мне очень помогли!

Я выбрал ваше альтернативное решение:

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

... и все работает отлично!

Хорошо видно!

См. Решение кода ниже:

private List<List<Object>> splitList(List<Object> list, int subListSize) {

List<List<Object>> splittedList = new ArrayList<>();

if (!CollectionUtils.isEmpty(list)) {

    int i = 0;
    int nbItems = list.size();

    while (i < nbItems) {
        int maxLastSubListIndex = i + subListSize;
        int lastSubListIndex = (maxLastSubListIndex > nbItems) ? nbItems : maxLastSubListIndex;
        List<Object> subList = list.subList(i, lastSubListIndex);
        splittedList.add(subList);
        i = lastSubListIndex;
    }
}

return splittedList;
}


private void indexDocumentsByEntityIds(Class<Object> clazz, String entityIdPropertyName, List<Object> ids) {

Session session = entityManager.unwrap(Session.class);

List<List<Object>> splittedIdsLists = splitList(ids, 128);

for (List<Object> splittedIds : splittedIdsLists) {

    FullTextSession fullTextSession = Search.getFullTextSession(session);
    fullTextSession.setFlushMode(FlushMode.MANUAL);
    fullTextSession.setCacheMode(CacheMode.IGNORE);

    Transaction transaction = fullTextSession.beginTransaction();

    CriteriaBuilder builder = session.getCriteriaBuilder();
    CriteriaQuery<Object> criteria = builder.createQuery(clazz);
    Root<Object> root = criteria.from(clazz);
    criteria.select(root).where(root.get(entityIdPropertyName).in(splittedIds));

    TypedQuery<Object> query = fullTextSession.createQuery(criteria);

    List<Object> results = query.getResultList();

    int index = 0;

    for (Object result : results) {
        index++;
        try {
            fullTextSession.index(result); //index each element
            if (index == splittedIds.size()) {
                fullTextSession.flushToIndexes(); //apply changes to indexes
                fullTextSession.clear(); //free memory since the queue is processed
            }
        } catch (TransientObjectException toEx) {
            LOGGER.info(toEx.getMessage());
            throw toEx;
        }
    }

    transaction.commit();
}
}
...