JDBC Pagination - PullRequest
       23

JDBC Pagination

33 голосов
/ 05 мая 2010

Я хочу реализовать нумерацию страниц с использованием JDBC. Фактически, я хочу знать: «Как я могу получить первые 50, а затем следующие 50 записей из базы данных для страниц 1 и 2 соответственно»

Мой запрос Select * from data [таблица данных содержит 20 000 строк]

Для страницы № 1 я получаю 50 записей, а для страницы № 2 я хочу получить следующие 50 записей. Как я могу эффективно реализовать это в JDBC?

Я искал и обнаружил, что rs.absolute(row) - это способ пропустить записи первой страницы, но для больших наборов результатов требуется некоторое время, и я не хочу терпеть это количество времени. Кроме того, я не хочу использовать rownum и limit + offset в запросе, потому что они не подходят для использования в запросе, я не знаю почему, но я не хочу использовать его в запросе.

Может кто-нибудь помочь мне, как ограничить ResultSet для нумерации страниц или есть какой-то способ, которым JDBC дает нам?

Ответы [ 13 ]

26 голосов
/ 05 мая 2010

Нет эффективного способа сделать это, просто используя JDBC. Вы должны сформулировать ограничение на n строк и , начиная с i-го элемента , непосредственно в SQL, чтобы оно было эффективным. В зависимости от базы данных это может быть довольно просто (см. Ключевое слово LIMIT в MySQL), в других базах данных, таких как Oracle, это может быть немного сложнее (включает в себя подзапрос и использование псевдостолбца rownum).

См. Это руководство по пагинации JDBC: http://java.avdiel.com/Tutorials/JDBCPaging.html

20 голосов
/ 05 мая 2010

Вы должны запросить только данные, которые на самом деле должны отображаться на текущей странице. Не перетаскивайте весь набор данных в память Java, а затем фильтруйте его там. Это только сделает вещи излишне медленными.

Если вам действительно трудно реализовать это правильно и / или вычислить запрос SQL для конкретной базы данных, взгляните на мой ответ здесь .

Обновление: , поскольку вы используете Oracle, вот отрывок, нацеленный на Oracle, из вышеупомянутого ответа:

В Oracle вам нужен подзапрос с предложением rownum, который должен выглядеть следующим образом:

private static final String SQL_SUBLIST = "SELECT id, username, job, place FROM"
    + " (SELECT id, username, job, place FROM contact ORDER BY id)"
    + " WHERE ROWNUM BETWEEN %d AND %d";

public List<Contact> list(int firstrow, int rowcount) {
    String sql = String.format(SQL_SUBLIST, firstrow, firstrow + rowcount);

    // Implement JDBC.
    return contacts;
}
9 голосов
/ 28 января 2013

Отказ от ответственности: Этот пост в блоге о разбиении на страницы SQL и JDBC размещен мной.

Независимо от нумерации страниц Hibernate, мы можем использовать нумерацию страниц SQL / пагинацию JDBC

SQL-нумерация страниц

Существует два основных подхода:

  1. работа с частичным набором результатов (новый запрос для каждой страницы)
  2. работает с полным набором результатов

Способ сделать это зависит от SQL

Для MySQL / многих других SQL это можно сделать с помощью limit и offset

Postgresql: http://microjet.ath.cx/WebWiki/ResultPaginationWithPostgresql.html

В Oracle он использует ту же форму, что и для обработки «запроса Top-N», например кто 5 самых высокооплачиваемых сотрудников, который оптимизирован

select *   from ( select a.*, rownum rnum

from ( YOUR_QUERY_GOES_HERE -- including the order by ) a

where rownum <= MAX_ROWS )

where rnum >= MIN_ROWS

Вот очень подробное объяснение ROW-NUM

Аналогично SO Thread

JDBC Pagination

Возникает вопрос: когда я выполняю SQL, как загружается результат? Сразу или по запросу? так же, как эта тема

Сначала нам нужно понять некоторые основы JDBC, начиная с Oracle

Для javadoc: Statement.execute ()

execute: Returns true if the first object that the query returns is a ResultSet object. Use this method if the query could return one or more ResultSet objects. Retrieve the ResultSet objects returned from the query by repeatedly calling Statement.getResutSet.

Доступ к данным в Resultset осуществляется с помощью курсора. Обратите внимание, что этот курсор отличается от курсора в DB, ​​в то время как он является указателем, первоначально расположенным перед первой строкой данных.

Данные извлекаются по запросу. в то время как при выполнении execute () вы выбираете в первый раз.

Тогда сколько данных загружено? Это настраивается. Можно использовать метод java API setFetchSize () в ResultSet, чтобы контролировать, сколько строк извлекается драйвером из БД за раз, насколько большие блоки он получает одновременно.

Например, предположим, что суммарный результат равен 1000. Если размер выборки равен 100, выборка 1-й строки загрузит 100 строк из БД, а 2-я - 100-я строка будет загружена из локальной памяти. Для запроса 101-й строки будут загружены еще 100 строк. в память.

Из JavaDoc

Gives the JDBC driver a hint as to the number of rows that should be fetched from the database when more rows are needed for ResultSet objects genrated by this Statement. If the value specified is zero, then the hint is ignored. The default value is zero.

Обратите внимание на слово «подсказка» - оно может быть переопределено конкретной реализацией драйвера.

Это также то, на чем основывается функция «Ограничение строк до 100» в клиенте, например, для разработчика SQL.

Завершая все решение, для прокрутки результатов необходимо учитывать типы ResultSet и ScrollableCursor в API

Пример реализации из этого поста можно найти в oracle

из книги Oracle Toplink: Руководство разработчика Пример 112 Размер выборки драйвера JDBC

ReadAllQuery query = new ReadAllQuery();

query.setReferenceClass(Employee.class);

query.setSelectionCriteria(new ExpressionBuilder.get("id").greaterThan(100));

// Set the JDBC fetch size

query.setFetchSize(50);

// Configure the query to return results as a ScrollableCursor

query.useScrollableCursor();

// Execute the query

ScrollableCursor cursor = (ScrollableCursor) session.executeQuery(query);

// Iterate over the results

while (cursor.hasNext()) {

System.out.println(cursor.next().toString());

}

cursor.close();

.....................

В конце концов, вопросы сводятся к

Какой лучший способ сделать нумерацию страниц?

Обратите внимание, что SQL должен быть ORDER, чтобы иметь смысл в подходе SQL,

В противном случае на следующей странице можно снова показать несколько строк.

Ниже приведены некоторые пункты из документации Postgresql по драйверу JDBC и другие ответы SO

Во-первых, исходный запрос должен иметь предложение ORDER BY, чтобы обеспечить разумную работу решения для пейджинга. В противном случае было бы совершенно правильным, чтобы Oracle возвращал те же 500 строк для первой страницы, второй страницы и N-й страницы

Основное отличие заключается в способе JDBC, который требуется для удержания соединения во время выборки. Это может быть неприемлемо, например, в веб-приложении без сохранения состояния.

Для SQL-пути

синтаксис специфичен для SQL и может быть нелегко поддерживать. Для JDBC way

  • Соединение с сервером должно осуществляться по протоколу V3. Это по умолчанию для (и поддерживается только) версий сервера 7.4 и потом.
  • Соединение не должно быть в режиме автоматической фиксации. Бэкэнд закрывает курсоры в конце транзакции, поэтому в режиме автоматической фиксации бэкэнд закроет курсор, прежде чем что-либо может быть получено из него.
  • Оператор должен быть создан с типом ResultSet ResultSet.TYPE_FORWARD_ONLY. Это значение по умолчанию, поэтому код не будетнужно переписать, чтобы воспользоваться этим, но это также означает, что вы не можете прокрутить назад или иначе прыгать в ResultSet.
  • Данный запрос должен быть одним оператором, а не множественным утверждения, соединенные точкой с запятой.

Некоторое дальнейшее чтение

Этот пост о настройке производительности с оптическим размером выборки

2 голосов
/ 12 февраля 2016

Я знаю, что этот вопрос старый, но так я реализовал пейджинг, надеюсь, кому-то это поможет

int pageNo = ....;    
String query = "SELECT * FROM data LIMIT ";

switch (pageNo) {
case 0: // no pagination, get all rows
    query += Integer.MAX_VALUE; // a big value to get all rows
    break;
case 1: // get 1st page i.e 50 rows
    query += 50;
    break;
default:
    query += String.valueOf((pageNo-1)*50 + 1) ", " + String.valueOf(50);
    break;
}

PreparedStatement ps = connection.prepareStatement(query);
....

делает значение 50 константой с именем pageSize, чтобы ее можно было изменить на любое число

2 голосов
/ 09 июня 2010

Это ссылка на решение гибернации для разбивки на страницы: HQL - идентификатор строки для нумерации страниц

2 голосов
/ 05 мая 2010

Если вы используете MySQL или PostgreSQL limit и offset - ваши ключевые слова. MSSqlServer и Oracle имеют схожие функции, но я, кажется, немного более болезненный.

Для MySQL и PostgreSQL смотрите здесь:

http://www.petefreitag.com/item/451.cfm

Для Oracle посмотрите здесь:

http://www.oracle -base.com / форум / viewtopic.php? Е = 2 & т = 8635

1 голос
/ 08 июля 2013

Ввод:

  1. Пример информации о заказе (A2 или D3) (A / D по возрастанию / убыванию) + столбец
  2. Пример информации о заказе (A2 или D3) (A / D по возрастанию / убыванию) + столбец
  3. Значение фильтра
  4. начало строки
  5. начало строки
  6. максимальное количество строк

Результат:

  • Выбранные значения
  • Выбранная страница
  • Индекс строки в этом порядке
  • Отсчитать доступные данные. (Сохранить второй запрос)

Только преимущество Запрос для:

  • сумма доступных столбцов с этим фильтром
  • переносить только выбранную страницу из БД
  • правильно заказано без динамического sql

Недостатки:

  • Oracle Dependend

    выберите x. * Из ( выберите c.pk_field, c.numeric_a, c.char_b, c.char_c ROW_NUMBER () более (ORDER BY декодировать (?, 'A1', to_char (c.numeric_a, 'FM00000000'), 'A2', c.char_b, 'A3', c.char_c, 'A') asc, декодировать (?, 'D1', to_char (c.numeric_a, 'FM00000000'), 'D2', c.char_b, 'D3', c.char_c, 'A') desc, c.pk_field asc
    ) Как "idx", COUNT (*) НАД (ЗАКАЗАТЬ НА 1) "cnt" из myTable c где c.haystack =? ) x где x. "idx" между наибольшим (nvl (?, 1), 1) и nvl (?, 1) -1 +?

1 голос
/ 09 июня 2010
PreparedStatement pStmt = // ... however you make it
pStmt.setFetchSize( /* desired number of records to get into memory */ ); 

Обратите внимание, setFetchSize(int) - это только подсказка - в прошлый раз, когда я использовал его, например, с MySQL, он не был поддержан. Кратко оглядываясь на документацию по Oracle, похоже, что их JDBC поддерживает это. Я не стал бы цитировать меня на это, но стоит хотя бы попробовать; и да, этот ответ хрупок, но, возможно, будет меньше головной боли, чем реализация надежного решения.

По сути, вы можете отправить запрос на все, и вы только получаете размер выборки в память за раз (при условии, что вы не держитесь за предыдущие результаты). Таким образом, вы должны установить размер выборки равным 50, установить соединение / запрос, отобразить первые 50 результатов (вызывая другую выборку для следующего фрагмента вашего запроса) и т. Д.

1 голос
/ 11 мая 2010

Oracle поддерживает стандартную оконную функцию ROW_NUMBER () начиная с 8i, так что вы можете использовать это. Вы можете сделать это как параметризованный запрос, поэтому вам просто нужно установить начальный и конечный номера строк. Э.Г.

SELECT * 
   FROM ( SELECT *, ROW_NUMBER() ORDER BY (sort key) AS rowNumber FROM <your table name> ) AS data
WHERE 
   rowNumber>=:start AND
   rowNumber<:end

(Если вы не используете именованные параметры, замените: start /: end на местозаполнитель позиционного параметра '?')

См. ВЫБОР SQL, Функции окна в Википедии. В статье также перечислены другие БД, которые поддерживают стандартную оконную функцию ROW_NUMBER ().

1 голос
/ 05 мая 2010

Используете ли вы какой-либо ORM Framework, например, Hibernate или даже Java Persistence API, или просто обычный SQL?

Мой ответ тогда: используйте LIMIT и OFFSET http://www.petefreitag.com/item/451.cfm

Или зайдите через ROWNUM Operator Тогда вам нужна оболочка для вашего SQL, но по сути это

  select * from (select bla.*, ROWNUM rn from (
  <your sql here>
  ) bla where rownum < 200) where rn >= 150'
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...