Подтвердить что ты не робот

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?

4b9b3361

Ответ 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.
  • Заданный запрос должен быть одним оператором, а не несколькими заявления, нанизанные вместе с точками с запятой.

Некоторое дополнительное чтение

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

Ответ 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