wake-up-neo.com

JDBC-Paginierung

Ich möchte die Paginierung mit JDBC implementieren. Die eigentliche Sache, die ich wissen möchte, ist "Wie bekomme ich zuerst 50 und dann die nächsten 50 Datensätze aus der Datenbank für Seite 1 bzw. 2"

Meine Abfrage ist Select * from data [Datentabelle enthält 20.000 Zeilen]

Für Seite 1 bekomme ich 50 Datensätze und für Seite 2 möchte ich die nächsten 50 Datensätze erhalten. Wie kann ich es effizient in JDBC implementieren?

Ich habe gesucht und festgestellt, dass rs.absolute(row) die Möglichkeit ist, Datensätze der ersten Seite zu überspringen, aber bei großen Ergebnissätzen dauert es einige Zeit, und ich möchte diese Zeitspanne nicht aushalten. Ich möchte auch nicht rownum und limit + offset in der Abfrage verwenden, da diese in der Abfrage nicht gut zu verwenden sind. 

Kann mir jemand helfen, wie man einen begrenzten ResultSet für die Paginierung erhält, oder gibt es eine Möglichkeit, die uns JDBC gibt?

31
Zeeshan

Es gibt keine effiziente Möglichkeit, dies durch die Verwendung von JDBC zu erreichen. Sie müssen das Limit in n Zeilen und von der i-ten item - Klausel direkt in der SQL formulieren, damit es effizient ist. Abhängig von der Datenbank ist dies möglicherweise ziemlich einfach (siehe LIMIT -Schlüssel von MySQL). Bei anderen Datenbanken wie Oracle kann dies etwas komplizierter sein (Unterabfrage und Verwendung einer Pseudo-Spalte). 

Sehen Sie sich dieses JDBC-Paginierungs-Tutorial an: http://Java.avdiel.com/Tutorials/JDBCPaging.html

26
psp

Sie sollten only die Daten abfragen, die Sie tatsächlich auf der aktuellen Seite anzeigen möchten. Ziehen Sie nicht den gesamten Datensatz in den Speicher von Java und filtern Sie ihn dort. Es würde nur die Dinge unnötig langsamer machen.

Wenn Sie es wirklich schwer haben, dies richtig zu implementieren und/oder die SQL-Abfrage für die spezifische Datenbank zu finden, dann schauen Sie sich meine Antworthier an.

Update: Da Sie Oracle verwenden, finden Sie hier einen Oracle-gezielten Auszug aus der oben genannten Antwort:

In Oracle benötigen Sie eine Unterabfrage mit der Klausel rownum, die wie folgt aussehen sollte:

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;
}
18
BalusC

Haftungsausschluss: Dieser Blogeintrag zur SQL-Paginierung & JDBC-Paginierung wird von mir gepostet.

Wenn Sie die Paginierung im Ruhezustand nicht berücksichtigen, können Sie die SQL-Paginierung/JDBC-Paginierung verwenden

SQL-Paginierung

Es gibt zwei grundlegende Ansätze:

  1. auf stückweise Ergebnismenge arbeiten (Neue Abfrage für jede Seite)
  2. betrieb mit vollem Ergebnissatz

Der Weg ist SQL-spezifisch

Für MySQL/viele andere SQLs kann mit Limit und Offset gearbeitet werden

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

In Oracle wird dasselbe Formular verwendet, um "Top-N-Abfragen", z. Wer sind die 5 bestbezahlten Mitarbeiter, die optimiert werden

select *   from ( select a.*, rownum rnum

from ( YOUR_QUERY_GOES_HERE -- including the order by ) a

where rownum <= MAX_ROWS )

where rnum >= MIN_ROWS

Hier eine ausführliche Erklärung zu ROW-NUM

Ähnlicher SO Thread

JDBC-Paginierung

Es stellt sich die Frage: Wie wird das Ergebnis geladen, wenn ich die SQL-Anweisung ausführe? Sofort oder auf Anfrage das gleiche wie dieser SO - Thread

Zuerst müssen wir einige Grundlagen von JDBC verstehen, wie von Oracle .

Per Javadoc: Anweisung.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.

Auf Daten in Resultset greifen wir über einen Cursor zu. Beachten Sie, dass sich dieser Cursor von dem von DB unterscheidet, während er sich vor der ersten Datenzeile befindet.

Die Daten werden auf Anfrage abgerufen. während Sie das execute () ausführen, werden Sie zum ersten Mal abgerufen.

Wie viele Daten werden dann geladen? Es ist konfigurierbar . Sie können die Java-API-Methode setFetchSize () für ResultSet verwenden, um zu steuern, wie viele Zeilen der Treiber vom DB abgerufen und wie groß die Blöcke auf einmal abgerufen werden.

Angenommen, das Gesamtergebnis ist 1000. Wenn die Abrufgröße 100 ist, werden beim Abrufen der 1. Zeile 100 Zeilen aus DB geladen, und die 2. bis 100. Zeile wird aus dem lokalen Speicher geladen.

Aus 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.

Beachten Sie das Wort "Hinweis" - es kann durch die treiberspezifische Implementierung überschrieben werden. 

Darauf basiert auch die Funktion "Limit Rows to 100" in einem Client-basierten SQL-Entwickler.

Um die gesamte Lösung abzuschließen, um die Ergebnisse zu scrollen, müssen die ResultSet-Typen und der ScrollableCursor in der API berücksichtigt werden

Eine Beispielimplementierung aus diesem Beitrag finden Sie in Oracle

dies ist aus dem Buch Oracle Toplink Developer's Guide Beispiel 112 JDBC-Treiberabrufgröße

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();

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

Immerhin kochen die Fragen

Was ist der bessere Weg, um Paginierung zu machen?

Beachten Sie, dass die SQL-Anweisung ORDER sein sollte, um im SQL-Ansatz einen Sinn zu ergeben.

Andernfalls können auf der nächsten Seite noch einige Zeilen angezeigt werden.

Nachfolgend finden Sie einige Punkte aus der Dokumentation von Postgresql zu JDBC-Treiber und anderen SO Antworten

Zunächst müsste die ursprüngliche Abfrage eine ORDER BY-Klausel haben, damit die Paging-Lösung sinnvoll funktioniert. Andernfalls wäre es für Oracle vollkommen gültig, die gleichen 500 Zeilen für die erste Seite, die zweite Seite und die N-te Seite zurückzugeben

Der Hauptunterschied besteht in der JDBC-Methode. Es ist erforderlich, die Verbindung während des Abrufs zu halten. Dies ist beispielsweise für eine statuslose Webanwendung nicht geeignet.

Für SQL-Weg

die Syntax ist SQL-spezifisch und möglicherweise nicht einfach zu verwalten. Für JDBC-Methode

  • Die Verbindung zum Server muss das V3-Protokoll verwenden. Dies ist Der Standard für die Serverversionen 7.4 und (Und wird nur von diesen unterstützt). 
  • Die Verbindung darf sich nicht im Autocommit-Modus befinden. Das Backend Schließt Cursor am Ende von Transaktionen, sodass das Backend den Cursor im Autocommit-Modus geschlossen hat, bevor etwas von ihm abgerufen werden kann.
  • Die Anweisung muss mit dem ResultSet-Typ ResultSet.TYPE_FORWARD_ONLY erstellt werden. Dies ist die Standardeinstellung. Daher muss kein Code Umgeschrieben werden, um diese Funktion zu nutzen. Dies bedeutet jedoch auch, dass Sie nicht rückwärts scrollen oder auf andere Weise im
  • Die angegebene Abfrage muss eine einzelne Anweisung sein, nicht mehrere -Anweisungen mit Strichpunkten.

Einige weitere Lektüre

In diesem Beitrag geht es um die Leistungsoptimierung mit der optischen Abrufgröße .

9
vincentlcy

Wenn Sie MySQL oder PostgreSQL verwenden, sind limit und offset Ihre Schlüsselwörter. MSSqlServer und Oracle haben ähnliche Funktionen, aber ich scheine etwas schmerzhafter zu sein.

Für MySQL und PostgreSQL sehen Sie hier:

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

Für Oracle schauen Sie hier:

http://www.Oracle-base.com/forums/viewtopic.php?f=2&t=8635

2
Nils Schmidt

Ich weiß, dass diese Frage alt ist, aber so habe ich Paging implementiert. Ich hoffe, es hilft jemandem

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);
....

machen Sie den Wert 50 zu einer Konstante namens pageSize, damit Sie ihn in eine beliebige Zahl ändern können

2
HeisenBerg

Dies ist der Link zu einer Hibernate-Lösung für das Paginieren von Ergebnissen: HQL - Zeilenbezeichner für Paginierung

2
Abhishek Gupta

Ich verstehe implizit, dass Sie nicht möchten, dass die JDBC-Verbindung ein einziges gigantisches Resultset enthält, das Sie sehr lange geöffnet halten und bei Bedarf navigieren können.

Der übliche Ansatz ist das Hinzufügen der erforderlichen SQL, um nur eine Teilmenge der vollständigen Anforderung abzurufen, die sich leider von Datenbank zu Datenbank unterscheidet und Ihre SQL-Anweisungen herstellerabhängig macht. Wenn ich mich richtig erinnere, wird LIMIT mit MySQL verwendet. Fragen Sie nach dem passenden Sortiment für jede Anfrage.

Ich glaube auch, dass Hibernate Funktionen enthält, die es Ihnen ermöglichen, dies für HQL zu tun, aber ich bin damit nicht vertraut.

Verwenden Sie eine Art ORM-Framework wie Hibernate oder sogar Java Persistence API oder einfach nur SQL?

Meine Antwort dann: Benutze LIMIT und OFFSET http://www.petefreitag.com/item/451.cfm

Oder gehen Sie über den ROWNUM Operator Sie benötigen dann einen Wrapper um Ihre SQL, aber im Grunde ist es das

  select * from (select bla.*, ROWNUM rn from (
  <your sql here>
  ) bla where rownum < 200) where rn >= 150'
1
Lars
PreparedStatement pStmt = // ... however you make it
pStmt.setFetchSize( /* desired number of records to get into memory */ ); 

Hinweis: setFetchSize(int)ist nur ein Hinweis - das letzte Mal, als ich es zum Beispiel mit MySQL verwendete, wurde es nicht unterstützt. Wenn Sie sich die Oracle-Dokumentation kurz ansehen, sieht es so aus, als würden sie von JDBC unterstützt. Ich würde mich nicht dazu zitieren, aber es ist zumindest einen Versuch wert; und ja, diese Antwort ist fragil, kann aber weniger Kopfschmerzen verursachen als die Implementierung der robusten Lösung.

Im Wesentlichen können Sie die Anforderung für alles ausgeben, und Sie erhalten immer nur die Größe des Abrufs in den Speicher (vorausgesetzt, Sie behalten die vorherigen Ergebnisse nicht bei). Sie müssen also Ihre Abrufgröße auf 50 einstellen, Ihre Verbindung/Abfrage herstellen, die ersten 50 Ergebnisse anzeigen (wodurch der nächste Abruf für den nächsten Biss Ihrer Abfrage bewirkt wird) usw.

1
Carl

Eingang: 

  1. Bestellinformationsbeispiel (A2 oder D3) (A/D aufsteigend/absteigend) + Spalte
  2. Bestellinformationsbeispiel (A2 oder D3) (A/D aufsteigend/absteigend) + Spalte
  3. Filterwert
  4. startreihe
  5. startreihe
  6. maximale Anzahl von Reihen

Ergebnis:

  • Ausgewählte Werte
  • Ausgewählte Seite
  • Index der Zeile in dieser Reihenfolge
  • Verfügbare Daten abzählen. (Speichern Sie eine zweite Abfrage)

Nur Vorteil Abfrage für:

  • summe der verfügbaren Spalten mit diesem Filter
  • Übertragen Sie nur die ausgewählte Seite von DB
  • ohne dynamisches SQL richtig bestellt

Nachteil:

  • Oracle Abhängig

    wähle x. * aus ( wähle c.pk_field, c.numeric_a, c.char_b, c.char_c ROW_NUMBER () aus (ORDER BY decode (?, 'A1', to_char (c. numeric_a, 'FM00000000'), 'A2', c.char_b, 'A3', c.char_c, 'A') asc, decode (?, '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" von myTable c wobei c.haystack =? ) x wobei x. "idx" zwischen dem größten (nvl (1, 1), 1) und nvl (1, 1) -1 +?

1
SkateScout

Oracle unterstützt die standardmäßige Fensterfunktion ROW_NUMBER () seit 8i, sodass Sie diese Funktion verwenden können. Sie können dies als parametrisierte Abfrage tun, so dass Sie nur die Anfangs- und Endzeilennummern festlegen müssen. Z.B.

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

(Wenn Sie keine benannten Parameter verwenden, ersetzen Sie: start /: end mit dem Positionsparameter '?').

Siehe SELECT SQL, Window Functions auf Wikipedia . Der Artikel listet auch die anderen DBs auf, die die Standardfensterfunktion ROW_NUMBER () unterstützen.

1
mdma

Sie können die Methode unten verwenden. 

  1. Hier ist rowNo die Nummer der Startzeile, von der Sie wollen um n Anzahl Datensätze abzurufen.
  2. pageSize ist die Gesamtzahl der Datensätze, die Sie abrufen möchten

 public CachedRowSet getScrollableList (PreparedStatement prestmt, int rowNo) 
 {
 getPageSize (); 
 ResultSet rs = null; 
 CachedRowSet crs = null; 
 Versuchen
 {
 rs = prestmt.executeQuery (); 
 crs = new CachedRowSetImpl (); 
 crs.setPageSize (pageSize); 
 crs.populate (rs, rowNo); 
 } 
 catch (SQLException ex) 
 {
 logger.error ("Ausnahme in DatabaseInterface in der Methode getScrollableList ()):" + ex.getMessage (), ex); 
 } 
 endlich
 {
 // schließe rs 
 // schließe pstmt 
 } 
 Rücksprung crs; 
 } 



 
0
Vaibhav Sharma