Безопасная очистка Hibernate-сессии в середине крупной транзакции - PullRequest
31 голосов
/ 24 сентября 2010

Я использую 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() - это метод ленивой загрузки?

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

Ответы [ 2 ]

46 голосов
/ 24 сентября 2010

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

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

13.1.Пакетная вставка

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

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, делаяих отдельные лица.Если последующий вызов требует, чтобы объект был «присоединен», он потерпит неудачу.

1 голос
/ 11 ноября 2016

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

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

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