Скажем, у меня есть таблица с миллионами строк. Как правильно выполнить итерацию запроса к этой таблице при использовании JPA, чтобы у меня не было всего списка в памяти с миллионами объектов?
Например, подозреваю, что, если стол большой, взорвется следующее:
List<Model> models = entityManager().createQuery("from Model m", Model.class).getResultList();
for (Model model : models)
{
System.out.println(model.getId());
}
Действительно ли пагинация (цикл и обновление вручную setFirstResult()
/ setMaxResult()
) - лучшее решение?
Изменить : основной вариант использования, на который я нацелен, - это своего рода пакетное задание. Ничего страшного, если на запуск уходит много времени. Веб-клиент не задействован; Мне просто нужно «что-то сделать» для каждой строки, по одной (или нескольким маленьким N) за раз. Я просто стараюсь не хранить их все в памяти одновременно.
Ответы:
Страница 537 Java Persistence with Hibernate дает решение с использованием
ScrollableResults
, но, увы, только для Hibernate.Таким образом, кажется, что использование
setFirstResult
/setMaxResults
и итерации вручную действительно необходимо. Вот мое решение с использованием JPA:затем используйте его так:
источник
size() == 100
вместо этого пропускает один дополнительный запрос, который возвращает пустой списокЯ попробовал ответы, представленные здесь, но JBoss 5.1 + MySQL Connector / J 5.1.15 + Hibernate 3.3.2 с ними не работал. Мы только что перешли с JBoss 4.x на JBoss 5.1, так что пока мы придерживаемся его, и поэтому последняя версия Hibernate, которую мы можем использовать, - 3.3.2.
Добавление пары дополнительных параметров выполнило свою работу, и такой код работает без OOME:
Важнейшие строки - это параметры запроса между createQuery и scroll. Без них вызов «scroll» пытается загрузить все в память и либо никогда не завершается, либо выполняется до OutOfMemoryError.
источник
Вы не можете сделать это в прямом JPA, однако Hibernate поддерживает сеансы без сохранения состояния и прокручиваемые наборы результатов.
Мы регулярно обрабатываем с его помощью миллиарды строк.
Вот ссылка на документацию: http://docs.jboss.org/hibernate/core/3.3/reference/en/html/batch.html#batch-statelesssession
источник
Честно говоря, я бы посоветовал оставить JPA и придерживаться JDBC (но, конечно, использовать
JdbcTemplate
класс поддержки или что-то подобное). JPA (и другие поставщики / спецификации ORM) не предназначены для работы со многими объектами в рамках одной транзакции, поскольку они предполагают, что все загруженное должно оставаться в кэше первого уровня (отсюда и необходимость вclear()
в JPA).Также я рекомендую более низкоуровневое решение, потому что накладные расходы на ORM (отражение - это только верхушка айсберга) могут быть настолько значительными, что итерация по простому
ResultSet
, даже с использованием некоторой облегченной поддержки, как упомянуто,JdbcTemplate
будет намного быстрее.JPA просто не предназначен для выполнения операций с большим количеством сущностей. Вы можете играть с
flush()
/,clear()
чтобы избежатьOutOfMemoryError
, но подумайте об этом еще раз. Вы получаете очень мало, расплачиваясь за огромное потребление ресурсов.источник
flush()
/clear()
. Первый - IMHO, не предназначенный для целей пакетной обработки, а использование последовательности flush () / clear () пахнет дырявой абстракцией .Если вы используете EclipseLink I, используя этот метод, чтобы получить результат как Iterable
закрыть Метод
источник
Это зависит от типа операции, которую вы должны выполнить. Почему вы зацикливаете более миллиона строк? Вы что-то обновляете в пакетном режиме? Вы собираетесь показывать клиенту все записи? Вы подсчитываете статистику по найденным объектам?
Если вы собираетесь отображать миллион записей для клиента, пересмотрите свой пользовательский интерфейс. В этом случае подходящим решением является разбивка результатов на страницы и использование
setFirstResult()
иsetMaxResult()
.Если вы запустили обновление большого количества записей, вам лучше сделать его простым и удобным
Query.executeUpdate()
. При желании вы можете выполнить обновление в асинхронном режиме, используя управляемый сообщениями компонент или Диспетчер работ.Если вы вычисляете некоторую статистику по извлеченным объектам, вы можете воспользоваться функциями группировки, определенными спецификацией JPA.
В любом другом случае просьба уточнить :)
источник
SELECT m.id FROM Model m
затем итерацией по List <Integer>.Нет "правильного", что это делать, это не то, для чего предназначены JPA, JDO или любой другой ORM, прямой JDBC будет вашей лучшей альтернативой, так как вы можете настроить его для возврата небольшого количества строк в время и сбрасывайте их по мере использования, поэтому курсоры на стороне сервера существуют.
Инструменты ORM не предназначены для массовой обработки, они предназначены для того, чтобы вы могли манипулировать объектами и пытаться сделать РСУБД, в которой хранятся данные, максимально прозрачной, большинство из которых терпят неудачу в прозрачной части, по крайней мере, в некоторой степени. В этом масштабе невозможно обработать сотни тысяч строк (объектов), не говоря уже о миллионах, с помощью любого ORM и заставить его выполняться в любое разумное время из-за накладных расходов на создание экземпляров объекта, простых и простых.
Используйте соответствующий инструмент. Прямые JDBC и хранимые процедуры определенно найдут свое место в 2011 году, особенно в том, что они делают лучше, чем эти структуры ORM.
Втянуть миллион чего угодно, даже в простое
List<Integer>
, не будет очень эффективно, независимо от того, как вы это делаете. Правильный способ сделать то, о чем вы просите, - простойSELECT id FROM table
, установитьSERVER SIDE
(зависит от поставщика), установить курсорFORWARD_ONLY READ-ONLY
и перебрать его.Если вы действительно запрашиваете миллионы идентификаторов для обработки, вызывая для каждого из них какой-то веб-сервер, вам также придется выполнить некоторую параллельную обработку, чтобы это работало в разумное время. Вытягивание курсором JDBC и размещение нескольких из них одновременно в ConcurrentLinkedQueue, а также получение и обработка небольшого пула потоков (# CPU / Cores + 1) - единственный способ завершить вашу задачу на машине с любым " нормальный "объем ОЗУ, учитывая, что у вас уже заканчивается память.
См. Также этот ответ .
источник
Можно использовать еще один «трюк». Загружать только набор идентификаторов интересующих вас объектов. Скажем, идентификатор имеет тип long = 8bytes, тогда 10 ^ 6, список таких идентификаторов составляет около 8Мб. Если это пакетный процесс (по одному экземпляру за раз), то это терпимо. Затем просто повторите и сделайте свою работу.
Еще одно замечание - вы все равно должны делать это кусками - особенно если вы изменяете записи, иначе сегмент отката в базе данных будет расти.
Когда дело доходит до установки стратегии firstResult / maxRows - она будет ОЧЕНЬ ОЧЕНЬ медленной для результатов, далеких от вершины.
Также примите во внимание, что база данных, вероятно, работает в изолированном режиме чтения , чтобы избежать фантомного чтения идентификаторов загрузки, а затем загружать объекты один за другим (или 10 на 10 или что-то еще).
источник
Я был удивлен, увидев, что использование хранимых процедур не было более заметным в ответах здесь. Раньше, когда мне приходилось делать что-то подобное, я создавал хранимую процедуру, которая обрабатывала данные небольшими порциями, затем ненадолго засыпала, а затем продолжала. Причина спящего режима заключается в том, чтобы не перегружать базу данных, которая предположительно также используется для запросов в более реальном времени, таких как подключение к веб-сайту. Если базу данных больше никто не использует, то вы можете не спать. Если вам нужно убедиться, что вы обрабатываете каждую запись один раз и только один раз, вам нужно будет создать дополнительную таблицу (или поле) для хранения, какие записи вы обработали, чтобы обеспечить устойчивость при перезапусках.
Экономия производительности здесь значительна, возможно, на порядки быстрее, чем все, что вы могли бы сделать в мире JPA / Hibernate / AppServer, и ваш сервер базы данных, скорее всего, будет иметь собственный механизм курсора на стороне сервера для эффективной обработки больших наборов результатов. Снижение производительности связано с тем, что вам не нужно отправлять данные с сервера базы данных на сервер приложений, где вы обрабатываете данные, а затем отправляете их обратно.
У использования хранимых процедур есть некоторые существенные недостатки, которые могут полностью исключить это для вас, но если у вас есть этот навык в своем личном наборе инструментов и вы можете использовать его в такой ситуации, вы можете довольно быстро избавиться от таких вещей. .
источник
Чтобы расширить ответ @Tomasz Nurkiewicz. У вас есть доступ к сервису,
DataSource
который, в свою очередь, может предоставить вам соединениеВ вашем коде у вас есть
Это позволит вам обойти JPA для некоторых конкретных крупных пакетных операций, таких как импорт / экспорт, однако у вас все еще есть доступ к менеджеру сущностей для других операций JPA, если он вам нужен.
источник
Используйте
Pagination
Concept для получения результатаисточник
Я сам задавался вопросом. Вроде имеет значение:
Я написал Iterator, чтобы упростить замену обоих подходов (findAll vs findEntries).
Я рекомендую вам попробовать оба.
В итоге я не использовал свой итератор фрагментов (так что, возможно, он не был протестирован). Кстати, вам понадобятся коллекции Google, если вы хотите его использовать.
источник
С гибернацией есть 4 разных способа добиться желаемого. У каждого есть компромиссы в дизайне, ограничения и последствия. Я предлагаю изучить каждый и решить, какой из них подходит для вашей ситуации.
источник
Вот простой, прямой пример JPA (в Kotlin), который показывает, как вы можете разбивать на страницы произвольно большой набор результатов, читая порции по 100 элементов за раз, без использования курсора (каждый курсор потребляет ресурсы в базе данных). Он использует разбивку на страницы с помощью набора ключей.
См. Https://use-the-index-luke.com/no-offset для концепции разбивки на страницы набора ключей и https://www.citusdata.com/blog/2016/03/30/five-ways-to- paginate / для сравнения различных способов разбивки на страницы и их недостатков.
источник
Пример с JPA и NativeQuery, получающими каждый раз размер элементов с использованием смещений
источник