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

JPA Java Discussion :

Requêtes JPA très lentes


Sujet :

JPA Java

  1. #1
    Membre actif
    Profil pro
    Inscrit en
    Janvier 2009
    Messages
    12
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2009
    Messages : 12
    Par défaut Requêtes JPA très lentes
    Bonjour à tous,

    Je me permets d'ouvrir ce topic pour un soucis concernant le jpa.
    Pour vous situer le contexte, j'ai commencé le jpa (couplé au framework Primefaces) à l'arrivée dans mon service informatique il y a de cela quatre mois environ.
    Profitant de mon arrivée, mon équipe à voulu se lancer dans une nouvelle techno : le jpa, et c'est moi qu'ils ont envoyé en première ligne ! (d'où le fait que personne n'ai pu m'aider au sein de ma boite =/ ).
    Concrètement, j'ai une application faisant office de tableau de bord, qui vient piocher des infos sur une base de données Oracle (ces données étant générées depuis un autre logiciel).
    Au départ je n'avais pas de soucis de performances, mais le cache jpa me donnait des soucis (pas d'actualisation des données nouvellement ajoutées depuis l'autre application, non prise en compte de la modification de données par les autres utilisateurs, etc.), par conséquent la seule solution que j'ai pu trouver a été de faire un tour dans le persistence.xml et de désactiver le cache. Bien entendu les performances en ont pris un coup, mais c'est le seul moyen que j'ai trouvé
    pour faire fonctionner correctement l'application.

    Le hic désormais, c'est que toutes mes requêtes vers la base de données via mon entity manager (requêtes de type JPQL) sont extremement lentes.
    Je m'explique : j'ai une table que je nommerai document_element (devant garder une certaine confidentialité, je ne peux malheureusement pas être plus précis) qui contient une référence vers un document et un element (en gros l’élément est un objet à analyser et le document est le document administratif qui y est associé, de plus un document peut recenser plusieurs éléments et un élément peut être référencé dans plusieurs documents), je cherche à récupérer un certain nombre
    de ces document_element pour en effectuer un count sur une page d'accueil (j'affiche des choses du genre : 'éléments en attente : 25', 'éléments validés:14', etc.).
    Mais chaque requête met environ 10 secondes à se réaliser, donc autant dire que cette page d'accueil prend presque une minute pour s'afficher, ce qui n'est pas normal pour des requêtes aussi simples.

    Au début j'ai pensé à revoir mon code, factoriser certaines parties histoire d'éviter de taper sur la base à plusieurs reprises pour la même opération. Mais même en faisant cela, je n'ai pas vu de changement significatif.
    Après j'ai tenté d'optimiser mes requêtes, rebelotte, aucune différence (remplacer les associations faites automatiquement par jpa, par des inner join ou autres).

    Du coup un collègue m'a suggéré de lancer la même requête mais en SQL depuis la console du soft SQL Developer, et bien je n'ai pas été déçu : la même requête s'est exécutée en 0,046 sec contre la dizaine de seconde en passant par jpa.
    J'ai alors supposé que jpa chargeait le contenu de chaque entité en entier (quelques centaines de milliers d'entrées) puis faisait les traitements dans son contexte de persistance (corrigez moi si je me trompe ). Du coup je me suis dit que j'allais gruger en créant de vues et au lieu de faire taper mon entité sur document_element, j'allais la rediriger vers une vue ayant fait une partie du traitement au préalable. Dans @Entity puis @Table(name=....) j'ai indiqué le nom de ma vue, mais on dirait que le jpa n'a pas réussi à gérer ça. A part me manger un java.permgen.space au bout de quelques minutes de recherches dans le vide, je n'ai pas eu grand chose.

    Bref, j'ai retourné le soucis dans tous les sens depuis trois jours et je ne sais vraiment pas s'il y a des choses à spécifier pour améliorer les perfs, des trucs à supprimer ou des options à activer. Voir même un moyen de conserver le cache jpa sans avoir de soucis d'actualisation. Mais si vous avez une solution, cela me sauverais la mise !

    Merci par avance pour vos lumières et bonne journée !

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

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

    Informations forums :
    Inscription : Avril 2007
    Messages : 25 482
    Par défaut
    Citation Envoyé par Oelth Voir le message
    je cherche à récupérer un certain nombre
    de ces document_element pour en effectuer un count sur une page d'accueil (j'affiche des choses du genre : 'éléments en attente : 25', 'éléments validés:14', etc.).
    Mais chaque requête met environ 10 secondes à se réaliser, donc autant dire que cette page d'accueil prend presque une minute pour s'afficher, ce qui n'est pas normal pour des requêtes aussi simples.
    Autrement dit, au lieu de demander à la DB de faire le count, tu demande à JPA de rapatrier toutes les données pour ensuite faire l'affichage de list.size(). Pas bien!


    Il y a une différence à faire un truc du genre

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    List<Document> docs = em.createQuery("from Document d where d.owner=:owner").setParamenter("owner","tartempion").getResultList();
    boolean valid = 0;
    for (Document d : docs) {
      for (Element e:docs.getElements()){
         if (e.isValidated()){
           valid++;
         }
      }
    }

    et faire

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Number n = (Number)em.createQuery("select count(e) from Document d inner join d.elements e where d.owner=:owner and e.validated = true").setParamenter("owner","tartempion").getUniqueResult();


    Au début j'ai pensé à revoir mon code, factoriser certaines parties histoire d'éviter de taper sur la base à plusieurs reprises pour la même opération. Mais même en faisant cela, je n'ai pas vu de changement significatif.
    Après j'ai tenté d'optimiser mes requêtes, rebelotte, aucune différence (remplacer les associations faites automatiquement par jpa, par des inner join ou autres).
    Avant d'optimiser, as-tu profilé tes requêtes pour savoir ce qui se passe. Il y a deux chose qui peuvent t'aider à comprendre où tu perds du temps:

    activer le debug dans ton driver sql: permettra de voir toutes les requetes avec paramètres faites à la DB => C'est dépendant du driver
    activer dans ton implémentation jpa l'affichage des requêtes, exemple avec le show sql de hibernate => Ca te permettra de voir comment ton entitymanager interprète tes requêtes.

    Vu que ta requête va vite en mode sql, je pense pas que l'optimsiation de stable ne soit en cause.
    Aussi question con: est-ce que tu n'aurais pas désactivé le lazy-loading dans tes relation one to many?


    J'ai alors supposé que jpa chargeait le contenu de chaque entité en entier (quelques centaines de milliers d'entrées) puis faisait les traitements dans son contexte de persistance (corrigez moi si je me trompe ).
    JPA charge ce que tu lui demande, pas plus. Mais une entité n'est jamais chargée à moitié. Par contre le nombre d'entité chargée est dépendant de ta requête JPQL. Si tu lui demande tout, il va tout prendre. Ta clause where doit être correcte. On n'ira pas plus loin si tu ne fournis pas de code.
    JE travaille sans cache de second niveau, et je n'ai aucun soucis. Quand au cache de premier niveau, je pense même pas qu'il soit désactivable, tellement il est nécessaire au fonctionnement de JPA. Comme il est associé à la transaction en cours, aucun problème de concurrence dedans.

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

    Informations forums :
    Inscription : Novembre 2006
    Messages : 7 313
    Billets dans le blog
    1
    Par défaut
    Est-ce que ton entity représentant le document aurait une propriété de type LOB ?
    Si c'est le cas, ça pourrait expliquer la lenteur du fait que l'attribut "lazy" ne fonctionne pas. Du coup, à chaque fois que tu charges l'entity, il charge les données du LOB.
    Pour éviter ce problème, il faut découper l'information en 2 tables (1 sur des informations de description, 1 sur le contenu), c'est ce que je fais et ça fonctionne très bien..
    N'oubliez pas de consulter les FAQ Java et les cours et tutoriels Java

  4. #4
    Membre actif
    Profil pro
    Inscrit en
    Janvier 2009
    Messages
    12
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2009
    Messages : 12
    Par défaut
    Salut et merci beaucoup pour votre aide !

    tchize_ :

    Effectivement en ce qui concernait le comptage je n'avais pas connaissance de cette deuxième façon de faire. En fait je fais effectivement un .size() de la liste retournée, mais j'avais tenté de remplacer ça par un COUNT dans ma requête, ce qui n'avais pas donné de résultats plus probants.

    Concernant le profilage des requêtes c'est justement quelque chose que je cherchais à faire, je me penche dessus dès demain. De la même manière je vérifierai pour le lazy loading dès que j'arriverai au boulot demain.

    Enfin, merci pour tes explications sur le fonctionnement des requêtes JPA. Normalement mes requêtes sont bien formées, après, de l'entité document ou element j'ai plusieurs entités liées (elles sont nécessaires pour d'autres applications que la mienne), je ne sais pas si elles peuvent impacter les performances et s'il y a moyen de préciser que je ne m'en sert pas quand je déclare mes entités. Concernant le code, je n'ai pas la main sur ce dernier aujourd'hui, je te transmets une des fonctionnalités "boiteuses" demain.

    OButterlin :

    Non, pas de propriété de type LOB. En revanche il y a plusieurs entités liées à un document et aussi à un élément (du genre, un document est associé à des entités salle, demandeur, etc.), du coup cela pourrait être une piste ?


    En tous cas, je vous remercie encore une fois pour le coup de main. Ça me permet d'y voir un peu plus clair sur mon problème et sur le JPA dans son ensemble !

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

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

    Informations forums :
    Inscription : Avril 2007
    Messages : 25 482
    Par défaut
    Citation Envoyé par Oelth Voir le message
    Effectivement en ce qui concernait le comptage je n'avais pas connaissance de cette deuxième façon de faire. En fait je fais effectivement un .size() de la liste retournée, mais j'avais tenté de remplacer ça par un COUNT dans ma requête, ce qui n'avais pas donné de résultats plus probants.
    Ralala, ne pas faire en logiciel ce que la base de donnée est capable de faire directement, de surcroit en utilisant ses index. Un count ne nécessite même pas que la DB accède aux table, elle est capable souvent de le faire juste en lisant l'index, ce qui est très performant.
    Citation Envoyé par Oelth Voir le message
    je ne sais pas si elles peuvent impacter les performances et s'il y a moyen de préciser que je ne m'en sert pas quand je déclare mes entités. Concernant le code, je n'ai pas la main sur ce dernier aujourd'hui, je te transmets une des fonctionnalités "boiteuses" demain.
    Tu n'es pas obligé de déclarer toutes les relations ni toutes les colonnes si tu ne t'en sert pas dans ton application.
    Citation Envoyé par Oelth Voir le message
    Non, pas de propriété de type LOB. En revanche il y a plusieurs entités liées à un document et aussi à un élément (du genre, un document est associé à des entités salle, demandeur, etc.), du coup cela pourrait être une piste ?
    Ouais si tu charge tous les documents pour faire le size et que rien n'est en lazy, au final tu charge toute la DB juste pour faire un comptage, c'est vraiment pas propre

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

    Informations forums :
    Inscription : Novembre 2006
    Messages : 7 313
    Billets dans le blog
    1
    Par défaut
    Peux-tu fournir les entités concernées par ta requête ?
    N'oubliez pas de consulter les FAQ Java et les cours et tutoriels Java

  7. #7
    Membre actif
    Profil pro
    Inscrit en
    Janvier 2009
    Messages
    12
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2009
    Messages : 12
    Par défaut
    Hello,

    Tout d'abord un grand merci à tous les deux.
    J'ai pour le moment retiré les entités inutiles et activé le lazy loading sur les relations one to many et j'ai déjà gagné une bonne dizaine de secondes sur le total de mes requêtes. Par contre si je prends chaque requête à part, elles me prennent toujours environ cinq secondes chacune.

    Pour ce qui est de faire des counts, je planche actuellement dessus (et je vais regarder au passage pour afficher la trace de mes requêtes).

    Alors concernant mes entités, les voici (par soucis de lisibilité et comme il ne s'agissait que de getter/setter, je me suis contenté d'indiquer les champs déclarés et pas les méthodes, s'il y en a besoin n'hésitez pas à me le dire):

    Entité DOCUMENT_ELEMENT
    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
     
    @Entity
    @Table(name = "DOCUMENT_ELEMENT")
     
    public class DocumentElement implements Serializable 
    {
        @Column(name = "DATE_DEBUT")
        @Temporal(TemporalType.DATE)
        private Date dateDebut;
     
        @Column(name = "DATE_FIN")
        @Temporal(TemporalType.DATE)
        private Date dateFin;
     
        @Column(name = "ETAT_PARTIE_SEQUENCE")
        private BigInteger etatPartieSequence;
        private static final long serialVersionUID = 1L;
     
        @EmbeddedId
        protected DocumentElementPK DocumentElementPK;
     
        @JoinColumn(name = "ELEMENTID", referencedColumnName = "ELEMENTID", insertable = false, updatable = false)
        @ManyToOne(optional = false, cascade = CascadeType.MERGE)
        private element element;
     
        @JoinColumn(name = "DOCUMENTID", referencedColumnName = "DOCUMENTID", insertable = false, updatable = false)
        @ManyToOne(optional = false)
        private document document;
     
    }

    Entité ELEMENT
    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
     
    @Entity
    @Table(name = "element")
    @XmlRootElement
    @NamedQueries({
        @NamedQuery(name = "element.findAll", query = "SELECT u FROM element u"),
        @NamedQuery(name = "element.findByelementid", query = "SELECT u FROM element u WHERE u.elementid = :elementid")})
     
    public class Element implements Serializable 
    {
        private static final long serialVersionUID = 1L;
        @Id
        @Basic(optional = false)
        @NotNull
        @Size(min = 1, max = 20)
        @Column(name = "elementID")
        private String elementid;
     
        @Size(max = 80)
        @Column(name = "ELEMENTDESC")
        private String elementdesc;
     
        @Column(name = "DATE_RECEPTION")
        @Temporal(TemporalType.DATE)
        private Date dateReception;
     
        @Size(max = 35)
        @Column(name = "DESTINATAIRES")
        private String destinataires;
     
        @Size(max = 80)
        @Column(name = "TYPE_ELEMENT")
        private String typeElement;
     
        @Size(max = 20)
        @Column(name = "POSSESSEUR")
        private String possesseur;
     
        @Size(max = 255)
        @Column(name = "COMMENTAIRE")
        private String commentaire;
     
        @Size(max = 10)
        @Column(name = "TYPE_ELEMENT")
        private String typeElement;
     
        @Column(name = "DATE_SORTIE")
        @Temporal(TemporalType.DATE)
        private Date dateSortie;
     
        @OneToMany(cascade = CascadeType.ALL, mappedBy = "element", fetch = FetchType.LAZY)
        private Collection<documentElement> documentElementCollection;
    }

    Entité DOCUMENT
    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
     
    @Entity
    @Table(name = "document")
    @XmlRootElement
    @NamedQueries({
        @NamedQuery(name = "Document.findAll", query = "SELECT u FROM document u"),
        @NamedQuery(name = "Document.findByDocumentid", query = "SELECT u FROM document u WHERE u.document = :documentid"),
        @NamedQuery(name = "Document.findByDocumentdesc", query = "SELECT u FROM document u WHERE u.documentdesc = :documentdesc")})
     
    public class Document implements Serializable {
     
        private static final long serialVersionUID = 1L;
     
        @Id
        @Basic(optional = false)
        @NotNull
        @Size(min = 1, max = 20)
        @Column(name = "DOCUMENTID")
        private String documentid;
     
        @Size(max = 80)
        @Column(name = "DESCRIPTION")
        private String description;
     
        @Size(max = 20)
        @Column(name = "SECTION_CONCERNEE")
        private String sectionConcernee;
     
        @Column(name = "DELAI")
        @Temporal(TemporalType.DATE)
        private Date delai;
     
        @Size(max = 20)
        @Column(name = "TYPE_DOCUMENT")
        private String typeDocument;
     
        @Size(max = 20)
        @Column(name = "IMPORTANCE")
        private String importance;
     
        @Size(max = 100)
        @Column(name = "STATUT_DOCUMENT")
        private String statutDocument;
     
        @Column(name = "DATE_CLOTURE")
        @Temporal(TemporalType.DATE)
        private Date dateCloture;
     
        @Column(name = "DATE_DEVIS")
        @Temporal(TemporalType.DATE)
        private Date dateDevis;
     
        @OneToMany(cascade = CascadeType.ALL, mappedBy = "Document", fetch = FetchType.LAZY)
        private Collection<documentElement> documentElementCollection;
     
    }


    Concernant la méthode en elle même, je l'appelle depuis ma vue via l'instruction suivante (le fameux .size() dont il faut que je me débarrasse ).

    Pour l'explication, j'ai pour chaque élément plusieurs documents associés (c'est le principe de mon application, ne pas prendre les éléments associés à un seul document) et je dois récupérer ceux associés à mon service ou à mes sous services (ça c'est pour le traitement commun à toutes mes méthodes).
    Ensuite pour la partie "A valider" présente ci dessous, je regarde l'état du document associé à mon service, s'il a déjà été validé je ne le prends pas, sinon je regarde si c'est à mon tour de le valider (auquel cas je le prends) ou si une autre section doit effectuer une validation de son propre document (associé au même élément) avant moi. Par ailleurs, je pourrai aussi voir des documents comme étant "A valider" dans le cas où mon service est le premier à devoir effectuer une validation.

    Vue
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     
    <p:panel id="waiting_for_validation" header="Documents à valider" style="margin: 4%; border-color: #0bb30b; border-width: 4px; border-radius: 0px!important; text-align: center" styleClass="dashboardPanel">
                        <p:commandButton
                            value="#{DocumentElementController.findWaitingForValidationDocuments(pUserServiceId).size()}"
                            style="margin: 25px; width: 180px; height: 180px; font-weight: bold; text-align: center; background : #0bb30b; border-color: #0bb30b; border-width: 5px; border-radius: 0px!important; font-size: 350% !important; text-align: center"
                            styleClass="dashboardButton"
                            action="/multi/waitingForValidation/List"
                            onstart="PF('loadingDialogVar').show()"
                            oncomplete="PF('loadingDialogVar').hide()"
                        />
    </p:panel>

    Contrôleur
    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
     
    //Contiendra l'ensemble des documents associés au service ou à ses sous
    //services et dont au moins un élément est en attente de validation par
    //un membre du service actuel
    private List<DocumentElement> waitingForValidationList = null;
     
     
    /**
         * Retourne la liste des documents associés au service dont l'id a été
         * passé en paramètre, et dont au moins un élément est en attente 
         * de validation de la séquence de la part de services étant avant 
         * le service ou les sous services de l'utilisateur actuel
         * @param pUserServiceId
         * @return 
    */
        public List<DocumentElement> findWaitingForValidationDocuments(int pUserServiceId)
        {
     
            if(waitingForValidationList == null)
            {
            waitingForValidationList =
                getFacade().findWaitingForValidationDocuments(pServiceId);
            }
     
            return waitingForValidationList;
        }

    Façade
    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
     
    /**
         * Retourne tous les documents associés au service et qui possèdent au
         * moins un élément multi sections, dont la séquence associée
         * est en attente de validation par ledit service
         * @param pServiceId
         * @return
         */
        public List<DocumentElement> findWaitingForValidationDocuments(int pServiceId)
        {
            List<Service> lServices;
            List<DocumentElement> lResult = new ArrayList<>();
            Query lFindWaitingForValidationDocumentsQuery;
            //Nom du service et de tous les sous services associés à ce dernier
            String lServiceEnumeration;
     
            //On récupère la hiérarchie des services dont on recherche les documents
            //contenant un élément multi services
            lServices = findServiceAndSubservices(pServiceId);
            //On récupère la liste des services à inclure dans les IN
            //Il s'agit d'une liste de type " 'DIRECTION', 'INFORMATIQUE', etc ".
            lServiceEnumeration = generateServicesEnumeration(lServices);
     
            //Dans un premier temps on recherche tous les documents associés à nos services
            //qui dont un des éléments du document est présent sur plusieurs documents
     
            //On fait un destinataires LIKE ('%-%') pour s'assurer qu'on attend bien plusieurs sections
            //Dans certains il existe plusieurs documents pour un éléments mais il s'agit d'erreurs de suppression de la 
            //par de l'administration, d'où la nécessité de vérifier cela
     
            //etatPartieSequence permet de savoir si un document a été validé on non. En gros : null correspond à un document non validé et 1 à un élément validé
            lFindWaitingForValidationDocumentsQuery= 
                em.createQuery(
                    "SELECT docelem "
                +   "FROM DocumentElement docelem "
                +   "WHERE docelem.Element.destinataires LIKE ('%-%') "
                +   "AND docelem.Element.possesseur IS NOT NULL "
                +   "AND docelem.etatPartieSequence IS NULL "
                +   "AND docelem.Document.sectionConcernee IN (" + lServiceEnumeration + ")");
     
            Iterator<DocumentElement> lIterator = 
                lFindWaitingForValidationDocumentsQuery.getResultList().iterator();
     
            while(lIterator.hasNext())
            {
                DocumentElement lTempDocumentElement = lIterator.next();
     
                //On regarde quelle est la première section qui doit valider la séquence
                String[] lDestinataires = 
                    lTempDocumentElement.getElement().getDestinataires().replace(" ", "").split("-");
                //Repère la position
                int lPosition = 0;
                boolean lFound = false;
     
                while((lPosition < lDestinataires.length) && (lFound == false))
                {
                    //Si le document est bien le premier de la séquence
                    if(lTempDocumentElement.getDocument().getSectionConcernee().equals(lDestinataires[lPosition]))
                    {
                        if(lPosition == 0)
                        {
                            lResult.add(lTempDocumentElement);
                        }
                        else
                        {
                            //On va regarder l'état du document présent juste
                            //avant celui que l'on recherche dans la séquence
                            Query lSubQuery = 
                                em.createQuery(
                                    "SELECT docelem "
                                +   "FROM DocumentElement docelem "
                                +   "WHERE docelem.Document.sectionConcernee = '"+lDestinataires[lPosition - 1]+"' "
                                +   "AND docelem.Element.elementid = '"+lTempDocumentElement.getElement().getElementid()+"'");
     
                            if(lSubQuery.getResultList().isEmpty() == false)
                            {
                                DocumentElement lSubResult = 
                                (DocumentElement) lSubQuery.getResultList().get(0);
     
                                //Si le service précédent le mien dans la séquence a validé son document
                                if(
                                    (lSubResult.getSequenceMultiValidee() != null)
                                &&  (lSubResult.getEtatPartieSequence().intValue() == 1))
                                {
                                    lResult.add(lTempDocumentElement);
                                }
                            }
     
                        }
                        lFound = true;
                    }
                    else
                    {
                        lPosition++;
                    }
                }
            }
     
            return lResult;
        }
    Je me doute que ma boucle de traitement n'aide pas à accélérer le traitement, mais même la requête hors de la boucle prend au minimum cinq secondes.
    Je suis en train de regarder par la même occasion comment améliorer les performances de cette boucle, mais je dois admettre avoir un peu de mal à trouver quoi que ce soit.

    Sinon j'espère avoir été assez clair dans ma présentation et dans mes explications. Si ce n'est pas le cas n'hésitez pas à me demander de reformuler ^^ .

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

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

    Informations forums :
    Inscription : Avril 2007
    Messages : 25 482
    Par défaut
    Plusieurs problèmes:

    ton EmbeddedId, je parie qu'il contient un truc genre un Document et un Element? Ca pose problème à JPA. Pour différentes raison (testé avec hibernate sous JPA 1 en tout cas), Document et Element sont initialisés systématiquement. Pas de lazy possible là dessus.
    Donc charger un DocumentElement dans ce cas là signifie aussi charger un Document et un Element. Pour corser le tout ce n'est pas fait en une seule requête.
    Tu va d'abord charger X DocumentElement (résultat de la requête) et ensuite charger X fois un Document et un Element. Pire des cas: 1+2*X requêtes SQL . Quand c'est possible éviter d'utiliser une entité comme clé primaire d'un autre.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
     lServiceEnumeration = generateServicesEnumeration(lServices)
    J'ose espérer que cette méthode ne fait pas requête?
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    Query lSubQuery = 
                                em.createQuery(
                                    "SELECT docelem "
                                +   "FROM DocumentElement docelem "
                                +   "WHERE docelem.Document.sectionConcernee = '"+lDestinataires[lPosition - 1]+"' "
                                +   "AND docelem.Element.elementid = '"+lTempDocumentElement.getElement().getElementid()+"'");
    Pour chaque DocumentElement (X), pour chaque Destinataire (Y), tu récupère encore une nouvelle série de DocumentElement (Z), avec le même problème que ci dessus (1+2*Z) requêtes.

    Le nombre de requêtes SQL est donc

    1+2*X+X*Y*(1+2*Z)

    Tu as donc un nombre de requêtes de l'ordre du n^3, tout ça pour... afficher la taille. Ton truc va s'effondrer à la première montée en charge.

  9. #9
    Membre actif
    Profil pro
    Inscrit en
    Janvier 2009
    Messages
    12
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2009
    Messages : 12
    Par défaut
    Alors effectivement oui, le EmbeddedId contient un Document et un Element ^^" .
    En fait lorsque j'ai commencé à utiliser le jpa sous netbeans je me suis appuyé sur les méthodes de génération automatique des entités et j'ai obtenu cette entité DocumentElement. Du coup si j'ai bien compris tu me suggèrerais de la supprimer et de faire mes requêtes avec des jointures comme en SQL classique ?

    Pour la deuxième partie, non ne t'inquiète pas je ne fais pas ça . J'ai créé une table "section" pour mon application où j'ai repris les noms de section existants (la base a été créée il y a de nombreuses années par des prestataires et elle ne contenait malheureusement pas de table équivalente...). Du coup il me suffit de piocher là dedans !

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

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

    Informations forums :
    Inscription : Avril 2007
    Messages : 25 482
    Par défaut
    Citation Envoyé par Oelth Voir le message
    Alors effectivement oui, le EmbeddedId contient un Document et un Element ^^" .
    En fait lorsque j'ai commencé à utiliser le jpa sous netbeans je me suis appuyé sur les méthodes de génération automatique des entités et j'ai obtenu cette entité DocumentElement. Du coup si j'ai bien compris tu me suggèrerais de la supprimer et de faire mes requêtes avec des jointures comme en SQL classique ?
    Non, plutot de donner un id à l'association.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    DocumentElement{
      @Id
      Integer id;
      @ManyToOne
      Document document
      @ManyToOne
      Element element
    //etc
    }
    Mais ce n'est qu'un problème parmis ta série. Tu fais beaucoup de chose que tu devrais faire directement dans la requête pour avoir ton comptage.

    Pire: en lisant le code, je ne comprends pas quel est le critère pour qu'un Document soit "waitingForValidation". Si cet état du document est important, pourquoi il n'y a pas directement une colonne "validated" ou "pendingValidation" dans ton objet Document?

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

    Informations forums :
    Inscription : Novembre 2006
    Messages : 7 313
    Billets dans le blog
    1
    Par défaut
    Un autre problème en passant :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    String[] lDestinataires =  lTempDocumentElement.getElement().getDestinataires().replace(" ", "").split("-");
    Il va de soit que tu ne respectes pas la première forme normale, tu devrais avoir une table de relation pour attacher tes destinataires.
    Si ça avait été le cas, tu aurais pu faire tout le traitement en une seule requête, en partant de cette table et en remontant vers DocumentElement...
    N'oubliez pas de consulter les FAQ Java et les cours et tutoriels Java

  12. #12
    Membre actif
    Profil pro
    Inscrit en
    Janvier 2009
    Messages
    12
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2009
    Messages : 12
    Par défaut
    tchize_ :

    Merci pour tes précisions, pour l'id je vais voir ce que je peux faire.

    Alors concernant les critères pour déterminer si le document est dans tel ou tel état, c'est un peu plus compliqué. Les tables Document/Element/DocumentElement ont été créées pour les besoins d'une application (un LIMS adapté aux besoins de notre structure par une société de prestation) qui se charge de faire des modifications sur lesdites tables. Et je n'ai pas la main sur ce logiciel, du coup je n'aurais pas la possibilité de mettre à jour le champ lors de traitements dans ce LIMS. En revanche, et suivant ta suggestion, je me dis que ça pourrait être pas mal de rajouter quelques triggers dans ma base histoire de faire une partie du traitement au préalable.

    Pour répondre à ta question, en fait il y a plusieurs étapes dans mon traitement de récupération :

    - Récupérer tous les documents associés à mon service ou mes sous services et possédant au moins un élément associé à plusieurs documents
    - Ne garder que les documents où dans l'association DocumentElement le champ etatPartieSequence vaut null (c'est à dire que le document n'a pas encore été validé)
    - A part de là, deux cas de figure :
    - Je récupère les documents en tête de liste des destinataires
    - Je récupère les documents qui ne sont pas en tête de liste des destinataires, mais où tous les destinataires précédents ont validé leur document

    OButterlin :

    Si seulement x) . Mais comme je l'ai dis un peu plus haut dans ce message, c'est une vieille base de données couplée à un logiciel sur lequel je n'ai pas la main. En bref, ces tables existaient même avant mon arrivée dans la boite (et peut être même avant celle de mes collègues actuels) et un peu tous les logiciels utilisés ici fonctionnent via cette base. Du coup je ne peux (malheureusement) pas y toucher comme ça. Remarque, ça me donne un bon argument pour justifier les performances de mon application

  13. #13
    Membre chevronné
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2005
    Messages
    241
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Février 2005
    Messages : 241
    Par défaut
    Bonjour,

    J'ai une question simple: ton environnement d'exécution ne serait-il pas partiellement la cause de lenteurs de traitements?
    En dehors de l'amélioration de la couche de persistance pour laquelle tchize_ et OButterlin t'apportent une aide précieuse, es-tu certain que ta JVM dispose du paramétrage suffisant pour exécuter les traitements de tes pages?
    Quel est le taux de sollicitation du GC pendant le traitement de la requête incriminée?

    Cordialement,
    Sébastien

Discussions similaires

  1. Requête PostgreSQL très lente
    Par adelinesc2 dans le forum Requêtes
    Réponses: 7
    Dernier message: 19/02/2015, 12h05
  2. Requête SQL très lente
    Par fishingman dans le forum VB.NET
    Réponses: 9
    Dernier message: 13/08/2012, 12h15
  3. [TopLink] couplé à JPA très lent
    Par willoi dans le forum JPA
    Réponses: 11
    Dernier message: 03/03/2008, 10h35
  4. Requête Access trés lente ?
    Par ghostdz dans le forum Bases de données
    Réponses: 4
    Dernier message: 03/08/2007, 08h41
  5. Réponses: 12
    Dernier message: 24/07/2007, 11h09

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