JDBC Pagination
Я хочу реализовать разбиение на страницы с использованием JDBC. Фактическая вещь, которую я хочу знать, - "Как я могу получить первые 50, а затем следующие 50 записей из базы данных для страниц 1 и 2 соответственно"
Мой запрос Select * from data
[таблица данных содержит 20 000 строк]
Для страницы # 1 я получаю 50 записей и для страницы # 2 хочу получить следующие 50 записей. Как я могу эффективно реализовать его в JDBC?
Я обыскал и обнаружил, что rs.absolute(row)
- это способ пропустить записи первой страницы, но для больших наборов результатов требуется некоторое количество времени, и я не хочу переносить это количество времени. Кроме того, я не хочу использовать rownum
и limit
+ offset
в запросе, потому что они не подходят для использования в запросе, я не знаю, почему, но я не хочу использовать его в запросе.
Может ли кто-нибудь помочь мне, как получить ограниченный ResultSet
для разбивки на страницы или есть ли какой-нибудь способ, который дает нам JDBC?
Ответы
Ответ 1
Нет эффективного способа сделать это, просто используя JDBC. Вы должны сформулировать предел для n строк и начать с предложений i-го элемента непосредственно SQL, чтобы он был эффективным. В зависимости от базы данных это может быть довольно просто (см. MySQL LIMIT-keyword), в других базах данных, таких как Oracle, это может быть немного сложнее (включает подзапрос и использование псевдополя rownum).
Смотрите это учебное пособие JDBC Pagination:
http://java.avdiel.com/Tutorials/JDBCPaging.html
Ответ 2
Вы должны запросить только данные, которые вам действительно нужны для отображения на текущей странице. Не перетаскивайте весь набор данных в память 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;
}
Ответ 3
Отказ от ответственности: Это сообщение в блоге о разбивке на страницы и разбиении на страницы JDBC размещено мной.
Без учета разбиения на страницы Hibernate мы можем использовать разбиение на страницы/разбиение на страницы /JDBC
разбиение на страницы <
Существуют два основных подхода:
- работающий по частному результирующему набору (новый запрос для каждой страницы)
- работает с полным набором результатов
Способ сделать это SQL-специфический
Для MySQL/многих других SQL это можно сделать с помощью ограничения и смещения
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
Разбивка JDBC
Возникает вопрос: когда я выполняю SQL, как загружается результат? Сразу или по запросу? такой же, как этот поток SO
Сначала нам нужно понять некоторые основы JDBC, как из Oracle
Per 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, чтобы контролировать, сколько строк извлекается из DB во времени драйвером, насколько большие блоки он извлекает сразу.
Например, предположим, что итоговый результат равен 1000. Если размер выборки равен 100, выборка первой строки будет загружать 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.
Обратите внимание на слово "подсказка" - его можно переопределить по конкретной реализации драйвера.
Это также то, что "Limit Rows to 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
- Подключение к серверу должно быть связано с протоколом V3. Это
по умолчанию для (и поддерживается только) версии сервера 7.4 и
позже.
- Соединение не должно быть в автоматическом режиме. Бэкэнд
закрывает курсоры в конце транзакций, поэтому в режиме autocommit
бэкэнд будет закрыт курсор, прежде чем что-нибудь можно будет извлечь
от него.
- Заявление должно быть создано с использованием типа ResultSet
ResultSet.TYPE_FORWARD_ONLY. Это значение по умолчанию, поэтому код не будет
необходимо переписать, чтобы воспользоваться этим, но это также означает
что вы не можете прокручивать назад или иначе прыгать в
ResultSet.
- Заданный запрос должен быть одним оператором, а не несколькими
заявления, нанизанные вместе с точками с запятой.
Некоторое дополнительное чтение
Этот пост посвящен настройке производительности с оптическим размером выборки
Ответ 4
Если вы используете MySQL или PostgreSQL limit и offset - это ваши ключевые слова. MSSqlServer и Oracle имеют схожие функции, но мне кажется, что это немного больнее.
Для MySQL и PostgreSQL смотрите здесь:
http://www.petefreitag.com/item/451.cfm
Для Oracle смотрите здесь:
http://www.oracle-base.com/forums/viewtopic.php?f=2&t=8635
Ответ 5
Это ссылка на решение для спящего режима для разбивки на страницы:
HQL - идентификатор строки для разбивки на страницы
Ответ 6
Я понимаю, что вы не хотите, чтобы соединение JDBC имело один гигантский набор результатов, который вы держите открытым в течение очень долгого времени и перемещаетесь по мере необходимости.
Обычный подход заключается в том, чтобы добавить SQL, требуемый только для получения подмножества полного запроса, который, к сожалению, отличается от базы данных к базе данных, и сделает ваши спецификаторы SQL-спецификаций конкретными. Если я правильно помню, LIMIT используется с MySQL. Задайте соответствующий диапазон для каждого запроса.
Я также считаю, что Hibernate содержит функциональность, которая позволяет вам делать это для HQL, но я не знаком с ней.
Ответ 7
Используете ли вы какую-то ORM-платформу, такую как 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'
Ответ 8
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 на placeal parameter place??)
Смотрите SELECT SQL, функции Window в википедии.
В статье также перечислены другие БД, которые поддерживают стандартную функцию окна ROW_NUMBER().
Ответ 9
PreparedStatement pStmt = // ... however you make it
pStmt.setFetchSize( /* desired number of records to get into memory */ );
Примечание. setFetchSize(int)
- это только подсказка - в прошлый раз, когда я использовал его с MySQL, он не поддерживался. Оглядываясь кратко на документацию Oracle, похоже, что их JDBC действительно поддерживает. Я бы не стал процитировать меня по этому поводу, но, по крайней мере, стоит попробовать. и да, этот ответ является хрупким, но может быть достаточно меньше головной боли, чем внедрение надежного решения.
По существу, вы можете выдать запрос на все, и вы получаете только размер выборки в памяти за один раз (при условии, что вы не держитесь за предыдущие результаты). Таким образом, вы установили размер выборки на 50, сделаете свое соединение/запрос, отобразите первые 50 результатов (вызывая другую выборку для следующего укуса вашего запроса) и т.д.
Ответ 10
Вход:
- Пример информации заказа (A2 или D3) (A/D по возрастанию/убыванию) + столбец
- Пример информации заказа (A2 или D3) (A/D по возрастанию/убыванию) + столбец
- Значение фильтра
- стартовая строка
- стартовая строка
- Максимальное количество строк
Результат:
- Выбранные значения
- Выбранная страница
- Индекс строки в этом порядке
- Отсчет доступных данных. (Сохранить второй запрос)
Преимущество только Запрос для:
- сумма доступных столбцов с этим фильтром
- переносить выбранную страницу только с db
- правильно упорядочен без динамического sql
Неудобство:
-
Oracle Dependend
выберите x. * from ( выберите 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
) AS "idx", COUNT (*) OVER (ORDER BY 1) "cnt" из myTable c где c.haystack =?
) x, где x. "idx" между наибольшими (nvl (?, 1), 1) и nvl (?, 1) -1 +?
Ответ 11
Я знаю, что этот вопрос старый, но это то, как я реализовал пейджинг, надеюсь, что это поможет кому-то
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, поэтому ее можно изменить на любое число
Ответ 12
Следующие java-коды хорошо работают:
package paginationSample;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
/**
*
* @ Nwaeze Emmanuel (FUNAI, 2016)
*/
public class PaginationSample extends javax.swing.JFrame {
public void getRows() {
Connection con2;
Statement stmt2;
ResultSet rs2;
int j=0;
String sz="";
try {
// Load MS accces driver class
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
String url = "jdbc:odbc:Driver={Microsoft Access Driver (*.mdb,
*.accdb)};DBQ=" + "C:\\Database2.mdb";
try{
con2 = DriverManager.getConnection(url, "user", "");
System.out.println("Connection Succesfull");
try{
stmt2=con2.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,
ResultSet.CONCUR_UPDATABLE );
String sql="";
if (txtpage.getText().trim().equals("") ||
txtpagesize.getText().trim().equals("")){
}else{
int pagesize=Integer.parseInt(txtpagesize.getText());
int page=Integer.parseInt(txtpage.getText());
sql="SELECT * FROM FSRegistration100L WHERE SN >= " +
(pagesize*page-pagesize+1) + " AND " + "SN <= " + (pagesize*page);
rs2=stmt2.executeQuery(sql);
if (rs2.wasNull()){
}else{
while ( rs2.next()){
sz=sz + rs2.getString("RegNo") + "\n";
j++;
}
txta.setText(sz);
txta.append(" Total rows =" + Integer.toString(j));
}
}
stmt2.close();
con2.close();
}
catch (NullPointerException s){
System.err.println("Got an exception166! ");
System.out.println(s.getMessage());
}
} catch (SQLException e1) {
System.err.println("Got an exception1! ");
System.err.println(e1.getMessage());
}
} catch (ClassNotFoundException e2) {
System.err.println("Got an exception2! ");
System.err.println(e2.getMessage());
}
}
private void jButton1ActionPerformed(java.awt.event.ActionEvent
evt) {
// TODO add your handling code here:
getRows();
}
// Variables declaration - do not modify
private javax.swing.JButton jButton1;
private javax.swing.JLabel jLabel1;
private javax.swing.JLabel jLabel2;
private javax.swing.JScrollPane jScrollPane1;
private javax.swing.JTextArea txta;
private javax.swing.JTextField txtpage;
private javax.swing.JTextField txtpagesize;
// End of variables declaration