IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Taglibs Java Discussion :

OutOfMemory, Lenteur chargement [DisplayTag]


Sujet :

Taglibs Java

  1. #1
    Membre du Club
    Inscrit en
    Juin 2008
    Messages
    125
    Détails du profil
    Informations forums :
    Inscription : Juin 2008
    Messages : 125
    Points : 57
    Points
    57
    Par défaut OutOfMemory, Lenteur chargement
    Bonsoir,

    Je rencontre deux principaux problèmes (qui pourraient être liés) :

    Problème 1 : Erreur de OutOfMemory
    Lorsque je tente d'afficher les élements d'une table contenant plus de 100000 lignes, je rencontre une erreur de OutOfMemory (et des fois Java heap space). Alors dans les forums, la réponse la plus fréquente est d'augmenter la mémoire allouée (-Xms512M -Xmx1024M dans mon cas) ce qui m'a permis de résoudre la même erreur pour arriver à afficher les 100000 lignes.
    J'ai bien entendu optimiser le mieux le code, forcer certains objets à nuls sans attendre que le garbage collector le fasse mais rien.
    Question : quelle est réellement la bonne solution ?

    Problème 2 : Lenteur chargement avec Displaytag
    Ca devrait sans doute être du au problème 1, mais quand je lance la recherche censer me retourner les 200000 lignes (60 lignes affichées au visiteur, le reste en pagination), quand l'affichage aboutit (pas de pb de memory) il met énormément de temps.
    Question : Est ce du au display tag ou à quoi ?

    Mon architecture :
    JDK : j2sdk1.4.2_13 (je sais c'est vieux mais l'appli est déjà déployée et mise en production)
    Tomcat 5.5.20
    Display tag 1.0
    Mysql 4
    Torque
    Merci.

  2. #2
    Expert éminent sénior
    Avatar de tchize_
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Avril 2007
    Messages
    25 481
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : Belgique

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Avril 2007
    Messages : 25 481
    Points : 48 806
    Points
    48 806
    Par défaut
    si tu n'affiche que 60 ligne, il n'y a pas nécessité de charger 200000 lignes de la db, limite la quantité de résultat. Mysql supporte le mot clé Limit x,y ou x et y sont respectivement le premier et le dernier résultat désiré (sous forme d'index). exemple

    select * from maTable limit 50,70

    récupèrera uniquement les lignes 50 à 70, ca va vachement limiter tes besoins mémoire.

  3. #3
    Modérateur
    Avatar de OButterlin
    Homme Profil pro
    Inscrit en
    Novembre 2006
    Messages
    7 310
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Novembre 2006
    Messages : 7 310
    Points : 9 522
    Points
    9 522
    Billets dans le blog
    1
    Par défaut
    Il y a un problème de conception, il faudrait être malade pour envoyer 100.000 lignes à un client web, personne ne les lira... sans compter que si tous tes utilisateurs s'amusent à faire la même requête et pour peu qu'en plus le résultat soit stocké en session... bonjours la tonne de mémoire

    Je te suggère de limiter le nombre d'enregistrements lorsque tu lances la requête, quitte à proposer de modifier ce nombre dynamiquement via l'ihm.
    Tu pourras utiliser la méthode setMaxRows(int) sur l'objet Statement (ou PreparedStatement) pour limiter les enregistrements.
    100 enregistrements (par défaut) me semblent confortables.
    Mieux vaut privilégier les options de filtrage pour que finalement, l'utilisateur n'ait que ce dont il a besoin.

    C'est mon avis
    N'oubliez pas de consulter les FAQ Java et les cours et tutoriels Java

  4. #4
    Membre du Club
    Inscrit en
    Juin 2008
    Messages
    125
    Détails du profil
    Informations forums :
    Inscription : Juin 2008
    Messages : 125
    Points : 57
    Points
    57
    Par défaut OutOfMemory, Lenteur chargement, Display Tag)
    Bonjour,

    Je suis tout à fait d'accord sur le principe de n'afficher que les lignes visisbles à l'utilisateur en utilisant notamment limit en sql. Par contre, je n'ai pas la possibilité de la faire dans ma requête sql. Je m'explique : j'utilise Display tag pour la gestion de la pagination. Ce framework s'attend à une la liste totale qu'il met en session et en fontion du lien de la page choisie, affiche le jeu d'enregistrement correspondant. Je suis donc obligé (à moins qu'il y ait une autre façon de faire avec Display tag) de récupérer toute la liste et la mettre en session.

    Merci pour votre réaction.

  5. #5
    Expert éminent sénior
    Avatar de tchize_
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Avril 2007
    Messages
    25 481
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : Belgique

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Avril 2007
    Messages : 25 481
    Points : 48 806
    Points
    48 806
    Par défaut
    La documentation de DisplayTag explique comment gérer le cas ou tu ne peux pas, pour des raisons de performances, fournir toute la liste

    http://displaytag.sourceforge.net/1....rtAndPage.html

  6. #6
    Membre du Club
    Inscrit en
    Juin 2008
    Messages
    125
    Détails du profil
    Informations forums :
    Inscription : Juin 2008
    Messages : 125
    Points : 57
    Points
    57
    Par défaut
    Citation Envoyé par tchize_ Voir le message
    La documentation de DisplayTag explique comment gérer le cas ou tu ne peux pas, pour des raisons de performances, fournir toute la liste

    http://displaytag.sourceforge.net/1....rtAndPage.html
    Merci pour le lien. Même si ça concerne plutôt la version 1.1 (je suis à la 1.0), je pense que ça pourrait résoudre mon problème (quitte à migrer vers la 1.1). J'y travaille et je vous tiens informés !

    Bonne journée.

  7. #7
    Membre à l'essai
    Profil pro
    Inscrit en
    Octobre 2009
    Messages
    8
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2009
    Messages : 8
    Points : 10
    Points
    10
    Par défaut
    Il existe des solutions AJAX comme le livegrid de RICO.

  8. #8
    Membre du Club
    Inscrit en
    Juin 2008
    Messages
    125
    Détails du profil
    Informations forums :
    Inscription : Juin 2008
    Messages : 125
    Points : 57
    Points
    57
    Par défaut
    Bonjour,

    J'étais trop penché sur la migration vers Display Tag 1.2 et la réadaptation des codes.

    Pour l'instant, j'ai résolu une partie de mes problèmes (celui de la mémoire) car avec la fonctionnalité partialList de Display Tag, je n'ai plus besoin de charger toute la liste en mémoire.

    Il me reste le problème de lenteur car malgré tout il y a un temps énorme de chargement avant que la page ne s'affiche. Pourtant, je n'ai que deux requêtes SQL qui s'éxecute : l'une pour connaître le nombre total d'enregistrements et l'autre pour récupérer uniquement la plage d'enregistrements souhaitée.

    Je vous tiens au courant une fois ce problème résolu. Si vous avez des idées ...

    A+

  9. #9
    Expert éminent sénior
    Avatar de tchize_
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Avril 2007
    Messages
    25 481
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : Belgique

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Avril 2007
    Messages : 25 481
    Points : 48 806
    Points
    48 806
    Par défaut
    Citation Envoyé par hisoft Voir le message
    Je vous tiens au courant une fois ce problème résolu. Si vous avez des idées ...

    A+
    difficile sans voir les requetes.

  10. #10
    Membre du Club
    Inscrit en
    Juin 2008
    Messages
    125
    Détails du profil
    Informations forums :
    Inscription : Juin 2008
    Messages : 125
    Points : 57
    Points
    57
    Par défaut
    Citation Envoyé par tchize_ Voir le message
    difficile sans voir les requetes.
    Ci-après la requête que j'exécute deux fois : la première fois sans ORDER BY et sans LIMIT qui me retourne le nombre total, la seconde avec ORDER BY et LIMIT. Le UNION ALL me permet de combiner le résultat sur 4 tables :TABLE_R, TABLE_R_SAV, TABLE_D, TABLE_D_SAV.

    Pour avoir le nombre total :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    nbTotal=0;
    while (resultSet.next()) 
      nbTotal++;
    Où est-il préférable de passer par count en SQL ?

    Requête SQL :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    SELECT AS_MSG, AS_DATE, AS_HEURE, AS_NUMEXP, AS_NUMDEST, AS_STATUT, 'E' as AS_MSGES 
    FROM TABLE_R R WHERE 1=1  AND DATE (R.date) >= '2009-10-01' AND DATE (R.date) <= '2009-10-22' 
     
    UNION ALL 
     
    SELECT AS_MSG, AS_DATE, AS_HEURE, AS_NUMEXP, AS_NUMDEST, AS_STATUT, 'E' as AS_MSGES 
    FROM TABLE_R_SAV R WHERE 1=1  AND DATE (R.date) >= '2009-10-01' AND DATE (R.date) <= '2009-10-22' 
     
    UNION ALL 
     
    SELECT AS_MSG, AS_DATE, AS_HEURE, AS_NUMEXP, AS_NUMDEST, AS_STATUT, 'S' as AS_MSGES 
    FROM TABLE_D D, TABLE_L L, TABLE_S S WHERE D.ref = L.ref AND S.chmp = L.chmp
    AND DATE (L.date) >= '2009-10-01' AND DATE (L.date) <= '2009-10-22' 
     
    UNION ALL 
     
    SELECT AS_MSG, AS_DATE, AS_HEURE, AS_NUMEXP, AS_NUMDEST, AS_STATUT, 'S' as AS_MSGES 
    FROM TABLE_D_SAV D, TABLE_L_SAV L, TABLE_S_SAV S WHERE D.ref = L.ref AND S.chmp = L.chmp
    AND DATE (L.date) >= '2009-10-01' AND DATE (L.date) <= '2009-10-22'
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    ORDER BY AS_DATE DESC  LIMIT 0, 60

  11. #11
    Modérateur
    Avatar de OButterlin
    Homme Profil pro
    Inscrit en
    Novembre 2006
    Messages
    7 310
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Novembre 2006
    Messages : 7 310
    Points : 9 522
    Points
    9 522
    Billets dans le blog
    1
    Par défaut
    Oui, assurément, il vaut mieux un count...

    Autre point, DATE(...) est une fonction évaluée sur chaque ligne, il serait préférable de tester la colonne date sous la forme
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    AND R.date >= '2009-10-01 00:00:00' AND R.date <= '2009-10-22 23:59:59'
    N'oubliez pas de consulter les FAQ Java et les cours et tutoriels Java

  12. #12
    Membre expérimenté
    Avatar de azerr
    Homme Profil pro
    Ingénieur Etude JEE/Eclipse RCP
    Inscrit en
    Avril 2006
    Messages
    942
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Drôme (Rhône Alpes)

    Informations professionnelles :
    Activité : Ingénieur Etude JEE/Eclipse RCP
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Avril 2006
    Messages : 942
    Points : 1 464
    Points
    1 464
    Par défaut
    Bonsoir,

    L'ideal est d'eviter d'executer un count pour obtenir le nombre total d'enregistrement. Pour effectuer ca (faut voir si ta version MySQL le supporte), on peut utiliser un Scrollable ResultSet. L'idee de cette technique est d'executer la requete et de positionner le curseur a la fin et d'obtenir le no de ligne :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    ResultSet rs = ...
    // move to the end of the result set
    rs.last();
    // get the row number of the last row which is also the row count
    int rowCount = rs.getRow();
    Le lien
    http://www.java2s.com/Code/Java/Data...tfromMySQL.htm te montre un exemple de code avec MySQL.

    Il y a 3 ans, j'avais gere cette problematique de pagination avec DisplayTag + Hibernate dans le projet GestCV. Je sais que tu n'es pas en Hibernate mais ca peut peut etre interesser qq1 . Voici la classe HibernatePage du projet qui gère cette problématique de scroll :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    package net.sourceforge.commons.spring.dao.hibernate;
     
    import java.util.List;
     
    import net.sourceforge.commons.dao.IPage;
     
    import org.apache.commons.logging.Log;
    import org.apache.commons.logging.LogFactory;
    import org.hibernate.HibernateException;
    import org.hibernate.Query;
    import org.hibernate.ScrollMode;
    import org.hibernate.ScrollableResults;
     
     
    /**
     * Pour mySQL ajouter parametre useServerPrepStmts=false a l'url JDBC
     * pour pouvoir utiliser limit dans le SELECT (genere par setFirstResult et setMaxResult)
     * ex : jdbc:mysql://localhost/gestcv?useServerPrepStmts=false
     * voir forum Hibernate : http://forum.hibernate.org/viewtopic.php?p=2230718
     * @author Angelo
     *
     */
    public class HibernatePage implements IPage {
     
    	private static Log log = LogFactory.getLog(HibernatePage.class);
     
    	private static boolean jdbcClassesSupportingScrollCursors; // Le driver JDBC supporte les curseurs avec scroll (par defaut false)
     
     
    	  protected List elements;
    	  protected int pageSize;
    	  protected int pageNumber;
    	  protected int totalElements = 0;
     
    	  //public static Set jdbcClassesSupportingScrollCursors = new HashSet();
    	  private ScrollableResults scrollableResults;
     
    	  private HibernatePage(int pageNumber, int pageSize) {
    	    this.pageNumber = pageNumber;
    	    this.pageSize = pageSize;
    	  }
     
     
    	  public boolean isFirstPage() {
    	    return getPageNumber() == 0;
    	  }
     
    	  public boolean isLastPage() {
    	    return getPageNumber() >= getLastPageNumber();
    	  }
     
    	  public boolean hasNextPage() {
    	    return elements.size() > getPageSize();
    	  }
     
    	  public boolean hasPreviousPage() {
    	    return getPageNumber() > 0;
    	  }
     
    	  public int getLastPageNumber() {
     
    	    double totalResults =
    	            new Integer(getTotalNumberOfElements()).doubleValue();
    	    return new Double(Math.floor(totalResults / getPageSize())).intValue();
    	  }
     
    	  public List getThisPageElements() {
    	/*
    	* Since we retrieved one more than the specified pageSize when the
    	* class was constructed, we now trim it down to the pageSize if a next
    	* page exists.
    	*/
    	    return hasNextPage() ? elements.subList(0, getPageSize()) : elements;
    	  }
     
    	  public void setThisPageElements(List thisPageElements) {
    		// TODO Auto-generated method stub
    		this.elements = thisPageElements;
    	  }
     
     
    	  /*public Logger getLogger() {
    	    return Tracer.hibernateLogger;
    	  }*/
     
    	  public int getTotalNumberOfElements() {
    	    return totalElements;
    	  }
     
    	  public int getThisPageFirstElementNumber() {
    	    return getPageNumber() * getPageSize() + 1;
    	  }
     
    	  public int getThisPageLastElementNumber() {
    	    int fullPage = getThisPageFirstElementNumber() + getPageSize() - 1;
    	    return getTotalNumberOfElements() < fullPage ?
    	            getTotalNumberOfElements() :
    	            fullPage;
    	  }
     
    	  public int getNextPageNumber() {
    	    return getPageNumber() + 1;
    	  }
     
    	  public int getPreviousPageNumber() {
    	    return getPageNumber() - 1;
    	  }
     
    	  public int getPageSize() {
    	    return pageSize;
    	  }
     
    	  public int getPageNumber() {
    	    return pageNumber;
    	  }
     
    	  public static HibernatePage getHibernatePageInstance(Query query, int pageNumber, int pageSize) {
     
    	      return getHibernatePageInstance(query, pageNumber, pageSize, jdbcClassesSupportingScrollCursors);
    	  }
     
     
    	  public static HibernatePage getHibernatePageInstance(Query query,
    	                                                       int pageNumber,
    	                                                       int pageSize,
    	                                                       boolean jdbcClassesSupportingScrollCursors)  {
    	    if (jdbcClassesSupportingScrollCursors)
    	      return HibernatePage.getScrollPageInstanceWithTotalByScroll(query, pageNumber, pageSize);
    	    else
    	      return HibernatePage.getScrollPageInstanceWithTotalByList(query, pageNumber, pageSize);
    	  }
     
    	  /**
               * Construct a new HibernatePage. HibernatePage numbers are zero-based so the
               * first page is page 0.
               *
               * @param query      the Hibernate Query
               * @param pageNumber the page number (zero-based);
               *                   if Integer.MAX_VALUE will return the last page for the query
               * @param pageSize   the number of results to display on the page
               */
    	  protected static HibernatePage getScrollPageInstanceWithTotalByScroll(Query query, int pageNumber, int pageSize) {
     
    	    HibernatePage sp = new HibernatePage(pageNumber, pageSize);
    	    try {
    	      sp.scrollableResults = query.scroll(ScrollMode.SCROLL_SENSITIVE);
    	      sp.scrollableResults.last();
    	      sp.totalElements = sp.scrollableResults.getRowNumber() + 1;
     
    	      sp.determineElements(query);
     
    	    } catch (HibernateException e) {
    	    	if (log.isErrorEnabled())
    	    		log.error("Failed to create ScrollPage by getScrollPageInstanceWithTotalByScroll: " + e.getMessage());
    	      throw e;
    	    }
     
    	    return sp;
    	  }
     
    	  /**
               * Construct a new HibernatePage. HibernatePage numbers are zero-based so the
               * first page is page 0.
               *
               * @param query      the Hibernate Query
               * @param pageNumber the page number (zero-based);
               *                   if Integer.MAX_VALUE will return the last page for the query
               * @param pageSize   the number of results to display on the page
               */
    	  protected static HibernatePage getScrollPageInstanceWithTotalByList(Query query, int pageNumber, int pageSize) {
     
    	    HibernatePage sp = new HibernatePage(pageNumber, pageSize);
     
    	    try {
     
    	      sp.scrollableResults = query.scroll(ScrollMode.FORWARD_ONLY);
    	      sp.totalElements = sp.calculateTotalElementsByList(query);
     
    	      sp.determineElements(query);
     
    	    } catch (HibernateException e) {
    	    	if (log.isErrorEnabled())
    	    		log.error("Failed to create ScrollPage by getScrollPageInstanceWithTotalByQuery: " + e.getMessage());
    	      throw e;
    	    }
     
    	    return sp;
    	  }
     
    	  private void determineElements(Query query) throws HibernateException {
     
    	    if (Integer.MAX_VALUE == this.pageNumber)
    	      this.pageNumber = (getTotalNumberOfElements() / this.pageSize);
     
    	    /**
                 * todo: this no more makes sense, as we know the total number of elements
                 *
                 * We set the max results to one more than the specfied pageSize to
                 * determine if any more results exist (i.e. if there is a next page
                 * to display). The result set is trimmed down to just the pageSize
                 * before being displayed later (in getThisPageElements()).
                 */
    	    elements = query.setFirstResult(this.pageNumber * this.pageSize).setMaxResults(this.pageSize + 1).list();
    	  }
     
    	  private int calculateTotalElementsByList(Query query) throws HibernateException {
    	    return query.list().size();
    	  }
     
     
    	public static boolean isJdbcClassesSupportingScrollCursors() {
    		return jdbcClassesSupportingScrollCursors;
    	}
     
     
    	public static void setJdbcClassesSupportingScrollCursors(
    			boolean jdbcClassesSupportingScrollCursors) {
    		HibernatePage.jdbcClassesSupportingScrollCursors = jdbcClassesSupportingScrollCursors;
    	}
    	}

    J'espere que ca pourra t'aider.

    Angelo

  13. #13
    Modérateur
    Avatar de OButterlin
    Homme Profil pro
    Inscrit en
    Novembre 2006
    Messages
    7 310
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Novembre 2006
    Messages : 7 310
    Points : 9 522
    Points
    9 522
    Billets dans le blog
    1
    Par défaut
    Je ne pense pas que ta solution soit la plus rapide lorsque tu traites une table énorme dans la mesure ou le last() provoquera (ou du moins risque de provoquer) la lecture de la totalité des enregistrements.
    Si tu en as 1000, ça ne doit pas trop se sentir, mais avec 500000, non seulement l'utilisation de la mémoire sera grande mais le temps de chargement sera conséquent.
    Personnellement, j'opte pour le count(*) qui a le gros avantage de ne renvoyer qu'un seul enregistrement et laisse la charge du SGBD de faire le calcul.
    N'oubliez pas de consulter les FAQ Java et les cours et tutoriels Java

  14. #14
    Membre expérimenté
    Avatar de azerr
    Homme Profil pro
    Ingénieur Etude JEE/Eclipse RCP
    Inscrit en
    Avril 2006
    Messages
    942
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Drôme (Rhône Alpes)

    Informations professionnelles :
    Activité : Ingénieur Etude JEE/Eclipse RCP
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Avril 2006
    Messages : 942
    Points : 1 464
    Points
    1 464
    Par défaut
    Bonjour OButterlin,

    Pour un ResultSet standard je suis d'accord avec toi, ca doit surement parcourir toute la table. Je ne l'ai pas mis en evidence mais je parlais de Scrollable ResultSet obtenu comme ceci :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    String query = "select id from employees";
    Statement stmt = conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,  ResultSet.CONCUR_READ_ONLY);
    rs = stmt.executeQuery(query);
    et la ca ne parcourt pas toute la table pour accéder au rs.last() (essayer avec ResultSet.TYPE_SCROLL_SENSITIVE aussi???)

    Tous les drivers JDBC ne supportent pas le Scrollable ResultSet, mais si il le supporte il vaut mieux utiliser cette technique, ce qui évite d'executer 2 requetes SQL.


    Angelo

  15. #15
    Modérateur
    Avatar de OButterlin
    Homme Profil pro
    Inscrit en
    Novembre 2006
    Messages
    7 310
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Novembre 2006
    Messages : 7 310
    Points : 9 522
    Points
    9 522
    Billets dans le blog
    1
    Par défaut
    En tout cas, ta méthode mérite le test (sur une grosse table) dans la mesure où elle aurait l'avantage de n'exécuter qu'une seule requête plutôt que 2.
    N'oubliez pas de consulter les FAQ Java et les cours et tutoriels Java

  16. #16
    Expert éminent sénior
    Avatar de tchize_
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Avril 2007
    Messages
    25 481
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : Belgique

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Avril 2007
    Messages : 25 481
    Points : 48 806
    Points
    48 806
    Par défaut
    Citation Envoyé par azerr Voir le message

    Tous les drivers JDBC ne supportent pas le Scrollable ResultSet, mais si il le supporte il vaut mieux utiliser cette technique, ce qui évite d'executer 2 requetes SQL.


    Angelo
    Même si ils le supportent, rien en garanti qu'ils le supportent en se basant le le SGBD pour faire le calcul. Rien n'empeche le driver de faire du cache en mémoire de tous les rows pour le gérer. De plus, même si le travail est laissé au sgbd, on ne connais pas les perfs du SGBD là dessus (on peux argumenter que c'est pas notre problème) qui pourrais vouloir cacher tous les rows du résultat en mémoire coté sgbd pour faire le scrolling...

    Ceci dit, je suis d'accord avec toi sur l'utilisation d'un scrollable resultset qui permet de ne faire qu'une seule requete, dans un seul cas, quand le select count(*) a un temsp d'exécution notable. Dans ces cas là, c'est le where qui prend du temps ou les join, mais dans tous les cas une partie de la requete qu'on devra refaire pour reprendre les rows si on utlise la technique "count"
    Bref si le select count(*) a un temps d'exécution notable, alors le scrollable resultset deviens intéressant, sinon mieux vaut utiliser le count, plus souple.

  17. #17
    Membre du Club
    Inscrit en
    Juin 2008
    Messages
    125
    Détails du profil
    Informations forums :
    Inscription : Juin 2008
    Messages : 125
    Points : 57
    Points
    57
    Par défaut
    Bonjour à tous,

    J'ai testé les différentes propositions et voici les résultats :

    - Date : j'ai remplacé les comparaisons de date (j'ai pas constaté le changement mais je suis d'accord sur le principe que la conversion ralentisse la requête)

    - Scrollable ResultSet : pratique car le prog. met moins de temps pour retrouver le nombre d'enregsitrements total, par contre il y a toujours le temps d'execution de la requête. Ensuite, dans mon cas (peut être que j'ai pas trop poussé la réflexion), j'exécute toujours 2 requêtes (nombre total d'enreg. + la liste des enreg.). En effet, dans mon action je fais :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    //...
    
    // Nombre total d'enregistrements
    nbTotal = StatMsgInterface.getNbreStatMsg(dateDeb, dateFin, numExp, typeMsg, modeTrait);
    			
    // Nombre d'enregistrements par page
    String nbEltsPageParam = request.getParameter(paramEncoder.encodeParameterName(TableTagParameters.SORT_AMOUNT_PAGE)); 
    int nbEltsPage = nbEltsPageParam != null ? Integer.parseInt(nbEltsPageParam) : 30;
    			
    // Numéro de page
    String pageParam = request.getParameter(paramEncoder.encodeParameterName(TableTagParameters.PARAMETER_PAGE)); 
    int page = pageParam != null ? Integer.parseInt(pageParam) : 1;
    
    // Bornes d'affichage
    int limitInf = nbEltsPage * (page-1);
    int limitSup = nbTotal>nbEltsPage + limitInf ? nbEltsPage + limitInf : nbTotal;
    			
    // Jeu d'enregistrements
    listeMsg = StatMsgInterface.getListStatMsg(dateDeb, dateFin, numExp, typeMsg, modeTrait, limitInf, limitSup);
    
    //...
    
    request.setAttribute("listeStatMsg",listeMsg);
    Vous remarquerez en gras l'appel des méthodes getNbreStatMsg et getListStatMsg (leur nom est parlant). Donc 2 requêtes.

    - Avec COUNT : sachant que jusqu'ici, je suis toujours contraint de lancer 2 requêtes, pour l'instant j'ai opté pour cette solution. En effet, le count met moins de temps pour me retourner le nombre total. Donc, le temps d'execution est lié plutôt à la requête me retournant la liste.
    Je met la requête SQL (pour ceux que ça intéresserait et s'il n'y a pas plus simple) :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     
    SELECT SUM(AS_TOTAL) FROM (
    SELECT COUNT(*) AS AS_TOTAL FROM TABLE_R R WHERE 1=1  AND R.date >= '2009-10-01 00:00:00' AND R.date <= '2009-10-22  00:00:00'  
    UNION ALL  
    SELECT COUNT(*) AS AS_TOTAL FROM TABLE_R_SAV R WHERE 1=1  AND  R.date >= '2009-10-01 00:00:00' AND R.date <= '2009-10-22 00:00:00'  
    UNION ALL  
    SELECT COUNT(*) AS AS_TOTAL FROM TABLE_D D, TABLE_L L, TABLE_S S WHERE D.ref = L.ref AND S.chmp = L.chmp
    AND L.date >= '2009-10-01 00:00:00' AND L.date <= '2009-10-22 00:00:00'  
    UNION ALL  
    SELECT COUNT(*) AS AS_TOTAL FROM TABLE_D_SAV D, TABLE_L_SAV L, TABLE_S_SAV S WHERE D.ref = L.ref AND S.chmp = L.chmp
    AND L.date >= '2009-10-01 00:00:00' AND L.date <= '2009-10-22 00:00:00'
    ) AS T_SELECT
    - Nombre d'enregistrement par page : en jouant sur ce nombre j'ai pu gagner un peu plus de temps lors du chargement (je passe de 60 lignes à 30).

    Enfin, je vais voir comment je peux ne lancer qu'une requête SQL et là peut être que le Scrollable ResultSet pourrait être plus intéressant.

    Merci pour votre aide et je reste à l'écoute.

  18. #18
    Modérateur
    Avatar de OButterlin
    Homme Profil pro
    Inscrit en
    Novembre 2006
    Messages
    7 310
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Novembre 2006
    Messages : 7 310
    Points : 9 522
    Points
    9 522
    Billets dans le blog
    1
    Par défaut
    Là le problème est simple.
    Pour n'exécuter qu'une seule requête, il suffit d'appeler une méthode qui renvoie un Objet complexe (ou une map par exemple) dans lequel tu auras le nombre et la liste.
    N'oubliez pas de consulter les FAQ Java et les cours et tutoriels Java

  19. #19
    Membre expérimenté
    Avatar de azerr
    Homme Profil pro
    Ingénieur Etude JEE/Eclipse RCP
    Inscrit en
    Avril 2006
    Messages
    942
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Drôme (Rhône Alpes)

    Informations professionnelles :
    Activité : Ingénieur Etude JEE/Eclipse RCP
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Avril 2006
    Messages : 942
    Points : 1 464
    Points
    1 464
    Par défaut
    Bonjour hisoft,

    J'ai pas bien compris si tu utilises la technique de Scrollable ResultSet, l'appel a StatMsgInterface.getNbreStatMsg devient obsolete et tu execute qu'une seule requete (StatMsgInterface.getListStatMsg), non?

    Si jamais tu es oblige de passer par un count(*) (StatMsgInterface.getNbreStatMsg), je te conseille de fusionner les 2 methodes StatMsgInterface.getNbreStatMsg et StatMsgInterface.getListStatMsg pour quelles utilisent la même Connection SQL. La tu as une ouverture/fermeture de connection SQL par appel de méthode.

    De plus je te conseille vivement de creer une classe de base qui gere la pagination. Avoir une couche service qui gere ca. Les actions Struts qui jouent le rôle de controlleur ne doivent pas avoir beaucoup de logique metier ou technique.

    J'avais fait un schema qui montre les couches Action, Services... sur http://gestcv.sourceforge.net/fr/architecture.html

    Angelo

  20. #20
    Membre du Club
    Inscrit en
    Juin 2008
    Messages
    125
    Détails du profil
    Informations forums :
    Inscription : Juin 2008
    Messages : 125
    Points : 57
    Points
    57
    Par défaut
    Citation Envoyé par OButterlin Voir le message
    Là le problème est simple.
    Pour n'exécuter qu'une seule requête, il suffit d'appeler une méthode qui renvoie un Objet complexe (ou une map par exemple) dans lequel tu auras le nombre et la liste.
    Au fait, comme je l'avais dit je n'avais pas trop poussé la réflexion. En effet, je lance 2 requêtes parce que l'une doit contenir LIMIT et l'autre non et parce que j'ai voulu que l'extraction de la plage d'enregistrement (LIMITINF, LIMITSUP) soit faite par le SGBD et non pas dans le code Java.

    Par contre, pour faire 1 seule requête, je dois fournir une requête SQL sans LIMIT (et donc j'aurai le nombre total), puis pour constituer la liste à afficher je boucle sur le ResultSet et je ne récupère que ce qui est entre LIMITINF et LIMITSUP.

    Toutefois, je préfère plutôt lancer 2 requêtes au lieu de boucler sur 1.000.000 de lignes pour ne récupérer que 30 !

+ Répondre à la discussion
Cette discussion est résolue.
Page 1 sur 2 12 DernièreDernière

Discussions similaires

  1. [SP-2010] Lenteur chargement fenêtre Modifier les propriétés d'un document
    Par shyangel dans le forum SharePoint
    Réponses: 5
    Dernier message: 29/07/2011, 10h21
  2. [WD15] passage WD14->15; même projet = lenteur chargement en mémoire
    Par chapeau_melon dans le forum WinDev
    Réponses: 6
    Dernier message: 21/10/2010, 21h47
  3. Lenteur chargement JPEG
    Par tnarol dans le forum Linux
    Réponses: 4
    Dernier message: 10/09/2009, 09h28
  4. Lenteur Chargement Hyper File
    Par ecoinfo dans le forum HyperFileSQL
    Réponses: 3
    Dernier message: 18/08/2009, 14h27
  5. Réponses: 3
    Dernier message: 30/06/2008, 22h52

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo