Безопасная очистка Hibernate-сессии в середине крупной транзакции

Я использую Spring + Hibernate для операции, которая требует создания и обновления буквально сотен тысяч элементов. Что-то вроде этого:

{
   ...
   Foo foo = fooDAO.get(...);
   for (int i=0; i<500000; i++) {
      Bar bar = barDAO.load(i);
      if (bar.needsModification() && foo.foo()) {
         bar.setWhatever("new whatever");
         barDAO.update(bar);
         // commit here
         Baz baz = new Baz();
         bazDAO.create(baz);
         // if (i % 100 == 0), clear
      }
   }
}

Чтобы защитить себя от потери изменений в середине, я фиксирую изменения сразу послеbarDAO.update(bar):

HibernateTransactionManager transactionManager = ...; // injected by Spring
DefaultTransactionDefinition def = new DefaultTransactionDefinition();
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
TransactionStatus transactionStatus = transactionManager.getTransaction(def);
transactionManager.commit(transactionStatus);

На данный момент я должен сказать, что весь процесс выполняется в транзакции, заключенной вorg.springframework.orm.hibernate3.support.ExtendedOpenSessionInViewFilter (да, это веб-приложение).

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

В среде только Hibernate это можно легко решить, вызвавorg.hibernate.Session#clear().

Теперь вопросы:

Когда это хорошее время дляclear()? Это имеет большую стоимость производительности?Почему не такие объекты, какbar или жеbaz выпущен / GCd автоматически? Какой смысл держать их в сеансе после фиксации (в следующем цикле итерации они все равно недоступны)? Я не сделал дамп памяти, чтобы доказать это, но у меня хорошее предчувствие, что они все еще там, пока полностью не выйдут. Если ответом на это является «Hibernate cache», то почему кеш не сбрасывается при уменьшении доступной памяти?это безопасно / рекомендуется позвонитьorg.hibernate.Session#clear() напрямую (имея в виду весь контекст Spring, такие вещи, как ленивая загрузка и т. д.)? Есть ли пригодные упаковщики / аналоги Spring для достижения того же?Если ответ на поставленный выше вопрос верный, что будет с объектомfooпредполагаяclear() вызывается внутри цикла? Что, еслиfoo.foo() такое метод отложенной загрузки?

Спасибо за ответы.

 stephen.hanson30 мар. 2012 г., 21:50
Я также делаю несколько больших вставок. Добавление кода для очистки и очистки сеанса иногда просто заставляло мой код работать в 4 раза быстрее!

Ответы на вопрос(2)

Я просто хотел указать, что после очистки сеанса, если вы хотите продолжать использовать некоторые объекты, которые были в сеансе, вам придетсяSession.refresh(obj) их, чтобы продолжить.

В противном случае вы получите следующую ошибку:

org.hibernate.NonUniqueObjectException
 Michael Lihs11 нояб. 2016 г., 00:15
пожалуйста выделите исходный код в вашем ответе `` для лучшей читаемости -Session.refresh(obj)
Решение Вопроса

Когда самое время очистить ()? Это имеет большую стоимость производительности?

Через равные промежутки времени, идеально совпадающие с размером пакета JDBC, после сброса изменений. Документация описывает общие идиомы в главе оПакетная обработка:

13,1. Пакетные вставки

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

Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();

for ( int i=0; i<100000; i++ ) {
    Customer customer = new Customer(.....);
    session.save(customer);
    if ( i % 20 == 0 ) { //20, same as the JDBC batch size
        //flush a batch of inserts and release memory:
        session.flush();
        session.clear();
    }
}

tx.commit();
session.close();

И это не должно иметь производительностьСтоимостьВ противоположность:

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

Почему объекты, такие как bar или baz, не высвобождаются / GCd автоматически? Какой смысл держать их в сеансе после фиксации (в следующем цикле итерации они все равно недоступны)?

Вам нужноclear() сеанс в явном виде, если вы не хотите отслеживать сущности, вот и все, вот как это работает (может потребоваться зафиксировать транзакцию без «потери» сущностей).

Но из того, что я вижу, инстансы bar и baz должны стать кандидатами в GC после ясности. Было бы интересно проанализировать дамп памяти, чтобы увидеть, что именно происходит.

безопасно / рекомендуется вызывать org.hibernate.Session # clear () напрямую

До тех пор, как выflush() ожидающие изменения, чтобы не потерять их (если это не то, что вы хотите), я не вижу никаких проблем с этим (ваш текущий код потеряет цикл создания каждые 100, но, возможно, это просто какой-то псевдокод).

Если ответ на поставленный выше вопрос верен, что произойдет с объектом foo, если предположить, что clear () вызывается внутри цикла? Что если foo.foo () - это метод отложенной загрузки?

призваниеclear() выселяет все загруженные экземпляры изSession, делая их обособленными сущностями. Если последующий вызов требует, чтобы объект был «присоединен», он потерпит неудачу.

 mindas24 сент. 2010 г., 17:33
Спасибо за разъяснение этого Паскаля!
 kommradHomer28 мар. 2012 г., 11:14
+1 за использование "напротив"
 Pascal Thivent24 сент. 2010 г., 17:39
@mindas Не за что.

Ваш ответ на вопрос