Пакетное обновление 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();
}
}
Когда процесс запускается, похоже, что некоторые объекты становятся отсоединенными, вызывая два признака:
- При попытке получить ленивые данные я получаю исключение:
org.hibernate.LazyInitializationException - could not initialize proxy - no Session
, - Когда не требуется ленивая загрузка - в БД обновляются только первые 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 просто чтобы прояснить это.