Spring — Hibernate улучшает производительность транзакций с помощью FlushMode

Я пытаюсь улучшить производительность моего транзакционного метода asynk.

В этой задаче мне нужно прочитать почти 7500 записей из таблицы, обработать ее и вставить/обновить соответствующую строку в другой таблице.

Я использую весенние данные jpa с спящим режимом.

Чтобы получить ScrollableResults, я внедряю EntityManager в свой сервис.

Вот как я получаю свой объект ScrollableResult:

Session session = (Session) em.unwrap(Session.class);
        ScrollableResults res = session.createQuery("from SourceTable s")
                .setCacheMode(CacheMode.IGNORE)
                .scroll(ScrollMode.FORWARD_ONLY);


while (res.next()){
.... // em.flush() called every 40 cycles
}

Цикл по результату занимает около 60 секунд.

И здесь узкое место. Если внутри моего цикла я выполняю простой запрос:

query = em.createQuery("from DestTable d where d.item.id = :id", DestTable.class);

 while (res.next()){
     query.setParameter("id", myId).getSingleResult();     
 }

Время выполнения становится в 10 раз медленнее... и занимает около 600 секунд.

Я пытался изменить параметр моего Session или моего EntityManager: session.setFlushMode(FlushModeType.COMMIT); em.setFlushMode(FlushModeType.COMMIT);

Это увеличивает производительность и удаляет метод ручного сброса (), работа выполняется за 40 секунд !!!

Итак, мои вопросы:

  • В чем разница вызова setFlushMode на session или на enityManager?
  • Почему setFlushMode(FlushModeType.COMMIT); увеличивается производительность таким образом, и я не могу иметь такую ​​​​же производительность, только вручную очищая entityManager?

person gipinani    schedule 28.01.2014    source источник


Ответы (1)


Проблема в том, что режим сброса по умолчанию — FlushModeType.AUTO. В режиме автоматической очистки Hibernate будет очищаться перед каждым запросом (только запросы, а не операции поиска). Это означает, что в приведенном выше примере по умолчанию Hibernate сбрасывается каждый раз, когда вы вызываете getSingleResult(). Причина, по которой он это делает, заключается в том, что возможно, что внесенные вами изменения повлияют на результаты вашего запроса, и поэтому Hibernate хочет, чтобы ваш запрос был как можно более точным, и сначала очищает его.

Вы не видите падения производительности в своем первом примере, потому что вы выдаете только один запрос и прокручиваете его. Лучшее решение, которое я нашел, - это то, что вы упомянули, - просто установить режим сброса на COMMIT. Не должно быть никакой разницы между вызовом setFlushMode на Session или EntityManager.

person Pace    schedule 28.01.2014
comment
Одно возможное различие, которое приходит на ум, заключается в том, что его использование в сеансе переопределяет свойство flush только для текущей транзакции, а при установке его в entityManager свойства устанавливаются для всех. Может быть? - person gipinani; 28.01.2014
comment
Нет, и EntityManager, и Session привязаны к контексту первого уровня. У них одинаковый жизненный цикл. Если бы вы смогли установить режим сброса на SessionFactory или EntityManagerFactory, то это могло бы быть так, но, насколько я знаю, это невозможно сделать. - person Pace; 28.01.2014
comment
Проблема с спящим сбросом заключается в том, что он запускает сборку очереди действий, а затем обрабатывает ее. Проблема становится более заметной, чем больше у вас управляемых объектов во время сброса. Спящий режим проверяет каждый! Экземпляр для изменений. Поэтому, если, как и в вашем случае, вы знаете, что ваш код не влияет на запрошенный список, решение с одним окончательным сбросом (при фиксации) может решить проблему. Другой вариант может состоять в том, чтобы вызвать очистку в entitymanager перед вашим запросом назначения, чтобы цикл сброса был как можно меньше. Побочным эффектом является меньший объем памяти. - person Martin Frey; 29.01.2014