web-dev-qa-db-fra.com

JDBC Pagination

Je souhaite implémenter la pagination à l'aide de JDBC. Ce que je veux savoir, c’est "Comment puis-je obtenir les 50, puis 50 prochains enregistrements de la base de données pour les pages 1 et 2 respectivement"

Ma requête est Select * from data [la table de données contient 20 000 lignes]

Pour la page 1, je reçois 50 enregistrements et pour la page 2, je veux obtenir les 50 prochains enregistrements. Comment puis-je l'implémenter efficacement dans JDBC?

J'ai cherché et trouvé que rs.absolute(row) est le moyen d'ignorer les enregistrements de première page, mais cela prend un peu de temps pour les ensembles de résultats volumineux et je ne veux pas supporter ce temps. De plus, je ne veux pas utiliser rownum et limit + offset dans une requête, car elles ne sont pas bonnes à utiliser dans une requête, je ne sais pas pourquoi, mais je ne veux pas l'utiliser dans une requête. 

Quelqu'un peut-il m'aider à obtenir une limite ResultSet limitée pour la pagination ou existe-t-il un moyen que JDBC nous donne?

31
Zeeshan

Il n’existe aucun moyen efficace de le faire en utilisant simplement JDBC. Vous devez formuler les limit à n lignes et démarrer directement à partir du i-ième élément dans le code SQL pour être efficace. En fonction de la base de données, cela peut être assez facile (voir le mot-clé LIMIT de MySQL), sur d'autres bases de données telles que Oracle, il peut être un peu plus compliqué (implique une sous-requête et l'utilisation de la pseudo colonne rownum). 

Voir ce didacticiel sur la pagination JDBC: http://Java.avdiel.com/Tutorials/JDBCPaging.html

26
psp

Vous devez interroger uniquement les données que vous réellement devez afficher sur la page en cours. Ne transportez pas l'intégralité du jeu de données dans la mémoire de Java et ne le filtrez pas ensuite. Cela ne ferait que ralentir inutilement les choses.

Si vous avez du mal à implémenter cela correctement et/ou à comprendre la requête SQL pour la base de données spécifique, jetez un œil à ma réponseici .

Update: puisque vous utilisez Oracle, voici un extrait de la réponse susmentionnée, destiné à Oracle:

Dans Oracle, vous avez besoin d'une sous-requête avec la clause rownum qui devrait ressembler à ceci:

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

Avertissement: Ce billet de blog sur la pagination SQL et la pagination JDBC est publié par moi. } _

Sans tenir compte de la pagination Hibernate, nous pouvons utiliser la pagination SQL/la pagination JDBC

Pagination SQL

Il y a deux approches de base:

  1. fonctionnement sur un ensemble de résultats fragmentés (Nouvelle requête pour chaque page)
  2. opérant sur l'ensemble des résultats

La façon de le faire est spécifique à SQL

Pour MySQL/de nombreux autres SQL, cela peut être fait avec limit et offset

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

Dans Oracle, il utilise le même formulaire que pour "Requête Top-N", par exemple. qui sont les 5 employés les mieux payés, ce qui est optimisé

select *   from ( select a.*, rownum rnum

from ( YOUR_QUERY_GOES_HERE -- including the order by ) a

where rownum <= MAX_ROWS )

where rnum >= MIN_ROWS

_ { Voici une explication très détaillée sur ROW-NUM } _

Similaire SO discussion

JDBC Pagination

La question qui vient à l’esprit est la suivante: quand j’exécute le code SQL, comment le résultat est-il chargé? Immédiatement ou sur demande? identique à ce fil SO

Nous devons d’abord comprendre quelques notions de base de JDBC, comme celles d’Oracle

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

Nous accédons aux données dans Resultset via un curseur. Notez que ce curseur est différent de celui de la base de données alors qu’il s’agit d’un pointeur initialement placé avant la première ligne de données.

Les données sont récupérées sur demande. pendant que vous exécutez execute (), vous récupérez pour la première fois.

Ensuite, combien de données sont chargées? C'est configurable . Vous pouvez utiliser la méthode setFetchSize () de l'API Java sur ResultSet pour contrôler le nombre de lignes extraites de la base de données par le pilote, ainsi que la taille des blocs qu'il extrait en même temps.

Supposons, par exemple, que le résultat total soit 1000. Si la taille d'extraction est 100, l'extraction de la 1re ligne chargera 100 lignes de la base de données et les lignes 2 à 100 de la mémoire locale. Pour interroger la 101e ligne, 100 autres lignes seront chargées en mémoire.

À partir de 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.

Notez le mot "indice" - il peut être remplacé par une implémentation spécifique au pilote. 

C’est également ce sur quoi repose la fonctionnalité "Limiter le nombre de lignes à 100" dans un client tel que le développeur SQL.

Pour compléter la solution complète, pour faire défiler les résultats, il est nécessaire de prendre en compte les types ResultSet et ScrollableCursor dans l'API.

On peut trouver un exemple d'implémentation de ce post dans Oracle

extrait du manuel Oracle Toplink Guide du développeur Exemple 112 Taille de récupération du pilote 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();

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

Après tout, les questions se résument à

Quel est le meilleur moyen de faire la pagination?

Notez que le SQL devrait être ORDER par pour donner un sens à l'approche SQL,

Sinon, il est possible d'afficher à nouveau certaines lignes dans la page suivante.

Vous trouverez ci-dessous quelques points de la documentation de Postgresql sur le pilote JDBC et d’autres réponses SO.

Tout d'abord, la requête d'origine doit comporter une clause ORDER BY pour que la solution de pagination fonctionne de manière raisonnable. Sinon, il serait parfaitement valide pour Oracle de renvoyer les mêmes 500 lignes pour la première page, la deuxième page et la Nième page.

La principale différence concerne la manière JDBC, il est nécessaire de maintenir la connexion pendant le chargement. Cela peut ne pas convenir dans une application Web sans état, par exemple.

Pour SQL

la syntaxe est spécifique à SQL et peut ne pas être facile à maintenir. Pour JDBC

  • La connexion au serveur doit utiliser le protocole V3. Ceci est La valeur par défaut pour les versions de serveur 7.4 et Ultérieures. 
  • La connexion ne doit pas être en mode autocommit. Le backend Ferme les curseurs à la fin des transactions. Ainsi, en mode de validation automatique, le backend aura fermé le curseur avant que rien ne puisse être récupéré
  • L'instruction doit être créée avec un type ResultSet de ResultSet.TYPE_FORWARD_ONLY. Ceci est la valeur par défaut, aucun code ne devra donc être réécrit pour en tirer parti, mais cela signifie également que vous ne pouvez pas faire défiler en arrière ni sauter dans le jeu de résultats
  • La requête donnée doit être une instruction unique et non pas plusieurs instructions Chaînées avec des points-virgules.

Quelques lectures complémentaires

_ { Cet article concerne le réglage des performances avec la taille de récupération optique } _

9
vincentlcy

Si vous utilisez MySQL ou PostgreSQL, limit et offset sont vos mots-clés. MSSqlServer et Oracle ont des fonctionnalités similaires, mais je semble être un peu plus douloureux.

Pour MySQL et PostgreSQL, regardez ici:

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

Pour Oracle, regardez ici:

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

2
Nils Schmidt

Je sais que cette question est ancienne, mais c’est ainsi que j’ai mis en place la pagination, j’espère que cela aidera quelqu'un

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

faire de la valeur 50 une constante nommée pageSize, afin qu'elle puisse être changée en un nombre quelconque

2
HeisenBerg

Ceci est un lien vers une solution de veille prolongée pour la pagination des résultats: HQL - identificateur de ligne pour la pagination

2
Abhishek Gupta

Je comprends implicitement que vous ne voulez pas que la connexion JDBC ait un seul jeu de résultats gigantesque que vous maintenez ouvert pendant très longtemps et que vous naviguez au besoin.

L'approche habituelle consiste à ajouter le code SQL nécessaire pour obtenir uniquement un sous-ensemble de la demande complète, qui diffère malheureusement d'une base de données à l'autre et rendra vos instructions SQL spécifiques au fournisseur. Si je me souviens bien, LIMIT est utilisé avec MySQL. Demandez la gamme appropriée pour chaque demande.

Je crois également que Hibernate contient des fonctionnalités qui vous permettent de le faire pour HQL, mais je ne les connais pas bien.

Utilisez-vous une sorte de structure ORM comme une mise en veille prolongée ou même une API de persistance Java ou simplement du SQL?

Ma réponse alors: Utilisez LIMIT et OFFSET http://www.petefreitag.com/item/451.cfm

Ou passez par l’opérateur ROWNUMVous avez besoin d’un wrapper autour de votre code SQL, mais

  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 */ ); 

Remarque, setFetchSize(int)n’est qu’un indice - la dernière fois que je l’utilisais avec MySQL, par exemple, il n’était pas pris en charge. En regardant brièvement la documentation Oracle, il semble que leur JDBC la supporte. Je ne citerais pas cela à ce sujet, mais ça vaut la peine d'essayer au moins; Et oui, cette réponse est fragile, mais cela pourrait suffire moins de maux de tête que la mise en œuvre de la solution robuste.

Pour l'essentiel, vous pouvez émettre la demande pour tout, et vous n'obtenez que la taille d'extraction en mémoire à la fois (à condition que vous ne conserviez pas les résultats précédents). Vous devez donc définir la taille de votre extraction à 50, établir votre connexion/requête, afficher les 50 premiers résultats (ce qui entraîne la récupération d'une autre extraction pour la suite de votre requête) et ainsi de suite.

1
Carl

Contribution: 

  1. Exemple d'information de commande (A2 ou D3) (A/D ascendant/descendant) + colonne
  2. Exemple d'information de commande (A2 ou D3) (A/D ascendant/descendant) + colonne
  3. Valeur de filtre
  4. ligne de départ
  5. ligne de départ
  6. nombre maximum de rangées

Résultat:

  • Valeurs sélectionnées
  • Page sélectionnée
  • Index de la ligne dans cet ordre
  • Comptez les données disponibles. (Enregistrer une seconde requête)

Avantage seulement Requête pour:

  • somme des colonnes disponibles avec ce filtre
  • transférer uniquement la page sélectionnée de la base de données
  • correctement commandé sans SQL dynamique

Désavantage:

  • Oracle Dependend

    sélectionnez x. * dans ( sélectionnez c.pk_field, c.numeric_a, c.char_b, c.char_c ROW_NUMBER () sur (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" à partir de myTable c où c.haystack =? ) x où x. "idx" entre le plus grand (nvl (?, 1), 1) et le nvl (?, 1) -1+?

1
SkateScout

Oracle prend en charge la fonction de fenêtre standard ROW_NUMBER () depuis 8i afin que vous puissiez l'utiliser. Vous pouvez le faire en tant que requête paramétrée, il vous suffit donc de définir les numéros de ligne de début et de fin. Par exemple.

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

(Si vous n'utilisez pas de paramètres nommés, remplacez: start /: end par l'espace réservé au paramètre de position '?')

Voir SELECT SQL, Fonctions de fenêtre sur wikipedia . Cet article répertorie également les autres DB prenant en charge la fonction de fenêtrage standard ROW_NUMBER ().

1
mdma

Vous pouvez utiliser la méthode ci-dessous pour cela. 

  1. Ici rowNo est le numéro de la ligne de départ à partir de laquelle vous souhaitezextraire n nombre d'enregistrements.
  2. taille de page est le nombre total d'enregistrements que vous voulez récupérer


 public CachedRowSet getScrollableList (PreparedStatement prestmt, int rowNo) 
 {
 getPageSize (); 
 ResultSet rs = null; 
 CachedRowSet crs = null; 
 essayer
 {
 rs = prestmt.executeQuery (); 
 crs = new CachedRowSetImpl (); 
 crs.setPageSize (pageSize); 
 crs.populate (rs, rowNo); 
 } 
 catch (SQLException ex) 
 {
 logger.error ("Exception dans DatabaseInterface dans la méthode getScrollableList ():" + ex.getMessage (), ex); 
 } 
 enfin
 {
 // ferme rs 
 // ferme pstmt 
 } 
 retour crs; 
 } 



 
0
Vaibhav Sharma