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?
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
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; }
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:
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 } _
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
Quelques lectures complémentaires
_ { Cet article concerne le réglage des performances avec la taille de récupération optique } _
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:
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
Ceci est un lien vers une solution de veille prolongée pour la pagination des résultats: HQL - identificateur de ligne pour la pagination
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'
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.
Contribution:
Résultat:
Avantage seulement Requête pour:
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+?
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 ().
Vous pouvez utiliser la méthode ci-dessous pour cela.
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; }