JPA: каков правильный шаблон для перебора больших наборов результатов? - PullRequest
109 голосов
/ 21 февраля 2011

Допустим, у меня есть таблица с миллионами строк.Используя JPA, как правильно перебрать запрос к этой таблице, чтобы У меня не было всего списка в памяти с миллионами объектов?

Например, яПодозреваю, что при большой таблице произойдет следующее:

List<Model> models = entityManager().createQuery("from Model m", Model.class).getResultList();

for (Model model : models)
{
     System.out.println(model.getId());
}

Является ли нумерация страниц (циклическое обновление и обновление вручную setFirstResult() / setMaxResult()) действительно лучшим решением?* Редактировать : основной вариант использования, на который я нацеливаюсь, является своего рода пакетной работой.Это хорошо, если это займет много времени, чтобы бежать.Там нет веб-клиента;Мне просто нужно «сделать что-то» для каждого ряда, по одному (или небольшому N) за раз.Я просто стараюсь не хранить их все в памяти одновременно.

Ответы [ 13 ]

0 голосов
/ 30 января 2014

В hibernate есть 4 различных способа достичь того, чего вы хотите.У каждого есть компромиссные решения, ограничения и последствия.Я предлагаю изучить каждый из них и решить, какой из них подходит для вашей ситуации.

  1. Использовать сеанс без сохранения состояния с scroll ()
  2. Использовать session.clear () после каждой итерации.Когда нужно присоединить другие объекты, загрузите их в отдельный сеанс.фактически первый сеанс эмулирует сеанс без сохранения состояния, но сохраняет все функции сеанса с сохранением состояния до тех пор, пока объекты не будут отсоединены.
  3. Используйте iterate () или list (), но в первом запросе получите только идентификаторы, затемв отдельном сеансе в каждой итерации выполните session.load и закройте сеанс в конце итерации.
  4. Используйте Query.iterate () с EntityManager.detach () aka Session.evict ();
0 голосов
/ 21 февраля 2011

Использование Pagination Концепция получения результата

0 голосов
/ 21 февраля 2011

Я задавался вопросом это сам.Кажется, имеет значение:

  • насколько велик ваш набор данных (строки)
  • какую реализацию JPA вы используете
  • какую обработку вы выполняете для каждой строки.

Я написал итератор, чтобы облегчить замену обоих подходов (findAll vs findEntries).

Я рекомендую попробовать оба.

Long count = entityManager().createQuery("select count(o) from Model o", Long.class).getSingleResult();
ChunkIterator<Model> it1 = new ChunkIterator<Model>(count, 2) {

    @Override
    public Iterator<Model> getChunk(long index, long chunkSize) {
        //Do your setFirst and setMax here and return an iterator.
    }

};

Iterator<Model> it2 = List<Model> models = entityManager().createQuery("from Model m", Model.class).getResultList().iterator();


public static abstract class ChunkIterator<T> 
    extends AbstractIterator<T> implements Iterable<T>{
    private Iterator<T> chunk;
    private Long count;
    private long index = 0;
    private long chunkSize = 100;

    public ChunkIterator(Long count, long chunkSize) {
        super();
        this.count = count;
        this.chunkSize = chunkSize;
    }

    public abstract Iterator<T> getChunk(long index, long chunkSize);

    @Override
    public Iterator<T> iterator() {
        return this;
    }

    @Override
    protected T computeNext() {
        if (count == 0) return endOfData();
        if (chunk != null && chunk.hasNext() == false && index >= count) 
            return endOfData();
        if (chunk == null || chunk.hasNext() == false) {
            chunk = getChunk(index, chunkSize);
            index += chunkSize;
        }
        if (chunk == null || chunk.hasNext() == false) 
            return endOfData();
        return chunk.next();
    }

}

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

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