Пакетное обновление Hibernate - объекты не обновляются

У меня есть пакетный процесс, который пересчитывает данные для набора объектов. Список сущностей извлекается из БД с помощью hibernate:

@Transactional(propagation = Propagation.REQUIRES_NEW)
public void recalculateUserData(Long userId){
    List<Entity> data = repository.getAllPendingRecalculation(userId);

    List<Entity> recalculated = new LinkedList();

    for (Entity entity : data){
        recalculateEntity(entity, recalculated);
        recalculated.add(entity);
        flushIfNeeded(recalculated); //every 10 records
    }
}

private void recalculateEntity(Entity entity, List<Entity> recalculated){
    //do logic
}

private void flush(){
    getSession().flush();
    getSession().clear();
}

private void flushIfNeeded(List<Entity> data) {
    int flushSize = 10
    if (data.size() % flushSize == 0){
        flush();
    }
}

Когда процесс запускается, похоже, что некоторые объекты становятся отсоединенными, вызывая два признака:

  1. При попытке получить ленивые данные я получаю исключение: org.hibernate.LazyInitializationException - could not initialize proxy - no Session,
  2. Когда не требуется ленивая загрузка - в БД обновляются только первые 10 записей, хотя flushIfNeeded(...) работает нормально.

С первой попытки я попытался решить ее, позвонив session#refresh(entity) внутри recalculateEntity(...) - это решило проблему отложенной инициализации, но проблема в #2 все еще возникла:

private void recalculateEntity(Entity entity){
    getSession().refresh(entity);
    //do logic
}

Так как это не решило проблему, я думал об использовании attach(entity) вместо refresh(entity):

private void recalculateEntity(Entity entity){
    getSession().attach(entity);
    //do logic
}

Кажется, это работает, но мой вопрос: почему эти сущности были отделены в первую очередь?

(Я использую Hibernate 3.6.10)


Обновить

Как объяснил @galovics:

Проблема в том, что вы очищаете весь сеанс, который содержит все ваши управляемые объекты, делая их отсоединенными.

В документации по пакетной обработке Hibernate указано, что пакетные обновления должны выполняться с использованием ScrollableResults (что решает эти проблемы), но в этом случае мне нужно извлечь все данные перед их обработкой, так как расчет сущностей может зависеть от уже вычисленных сущностей. Например, для расчета сущности № 3 могут потребоваться данные, рассчитанные для сущности № 1 и № 2.

Для такого случая было бы лучше использовать Session#attach(entity) (как показано в коде), используя Session#flush() без использования Session#clear() или есть лучшее решение?

1 ответ

Решение

Проблема в том, что вы очищаете весь сеанс, который содержит все ваши управляемые объекты, делая их отсоединенными.

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

Статья на LazyInitializationException просто чтобы прояснить это.

Другие вопросы по тегам