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

Hibernate Java Discussion :

Join fetch et clause "with" [HQL]


Sujet :

Hibernate Java

  1. #1
    Membre confirmé
    Homme Profil pro
    Développeur Web
    Inscrit en
    Décembre 2006
    Messages
    126
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : Belgique

    Informations professionnelles :
    Activité : Développeur Web

    Informations forums :
    Inscription : Décembre 2006
    Messages : 126
    Par défaut Join fetch et clause "with"
    Bonjour,

    Je me casse la tête sur le problèmes suivant :

    Mon entité Technicien possède une liste de Taches qu'il effectue.
    Ces taches pouvant être nombreuse puisse qu'on en garde l'historique, le lien entre les 2 entité est déclaré lazy.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    @Entity
    @Table(name="TECHNICIAN")
    public class Technician {
        @OneToMany(mappedBy="technician", fetch=FetchType.LAZY)
        private Set<Task> tasks = new HashSet<Task>();
        ...
    }
    Je souhaite dans une requête obtenir tout mes techniciens et leur associer les taches qu'il ont effectuées le jour J.

    J'écris donc :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    SELECT DISTINCT tch 
    FROM Technician tch 
    LEFT JOIN FETCH tch.tasks tsk 
    WITH tsk.scheduledStartDate >= :schedStart_begin AND tsk.scheduledStartDate <= :schedStart_end 
    WHERE tch.partner=:partner
    Le problème :
    org.hibernate.hql.ast.QuerySyntaxException: with-clause not allowed on fetched associations; use filters
    (A noter que sans le fetch, ma requête me renvoie le résultat attendu.)

    Si je ne peux pas utiliser de clause "with" avec un "fetch", comment puis-je filtrer mes taches tout en récupérant tous mes techniciens ? (y compris ceux qui n'ont pas de tache pour ce jour là).

    en espérant trouver un peu de lumière par ici

    D'avance je vous remercie

  2. #2
    Membre Expert
    Profil pro
    Inscrit en
    Août 2006
    Messages
    3 277
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2006
    Messages : 3 277
    Par défaut
    Désolé pour mon ignorance, mais pourquoi ne pas mettre l'équivalent du with dans la clause where ?
    Tu peux utiliser un left join, si tu veux aussi les techniciens sans taches.

  3. #3
    Expert éminent
    Avatar de tchize_
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Avril 2007
    Messages
    25 483
    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 483
    Par défaut
    Comme le dit le message d'erreur:

    Citation Envoyé par Ghurdyl Voir le message
    use filters



    http://docs.jboss.org/hibernate/core...l/filters.html

  4. #4
    Membre confirmé
    Homme Profil pro
    Développeur Web
    Inscrit en
    Décembre 2006
    Messages
    126
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : Belgique

    Informations professionnelles :
    Activité : Développeur Web

    Informations forums :
    Inscription : Décembre 2006
    Messages : 126
    Par défaut
    Merci pour vos réponses.

    @fr1man :
    Pas d'ignorance là dedans, c'était la première chose que j'ai testée avant de découvrir le mot-clé "with".
    Mais mettre l'équivalent du with dans la clause where, ne fonctionne pas. Le where s'applique sur tout les records (combinaison technician-task) après le join, ce qui fait que les records avec un technicien sans tâche (ou sans tâche pour la date donnée) se fait éjecter puisse qu'il ne satisfait pas les conditions du filtre. (En effet toute les colonnes venant de l'entité "task" sont nulles)

    @tchize_ :
    Ici c'est mon ignorance que je mets en avant, je n'utilise pas d'objet Session mais un EntityManager et ce comme dans tout mes autres DAO. Je voulais donc garder le même principe.
    Seulement, jusqu'à présent, je ne trouve pas comment spécifier un "filter" sur un EntityManager.
    Des conseils/astuces dans cette voie ?

  5. #5
    Membre Expert
    Profil pro
    Inscrit en
    Août 2006
    Messages
    3 277
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2006
    Messages : 3 277
    Par défaut
    Ok, alors essaie la combinaison du "where" + "left join", ça devrait le faire.

  6. #6
    Membre confirmé
    Homme Profil pro
    Développeur Web
    Inscrit en
    Décembre 2006
    Messages
    126
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : Belgique

    Informations professionnelles :
    Activité : Développeur Web

    Informations forums :
    Inscription : Décembre 2006
    Messages : 126
    Par défaut
    Citation Envoyé par fr1man Voir le message
    Ok, alors essaie la combinaison du "where" + "left join", ça devrait le faire.
    J'ai bien peur de ne pas comprendre
    Si tu veux dire :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    SELECT DISTINCT tch 
    FROM Technician tch 
    LEFT JOIN FETCH tch.tasks tsk 
    WHERE (tch.partner=:partner) 
    AND (tsk.scheduledStartDate >= :schedStart_begin AND tsk.scheduledStartDate <= :schedStart_end)
    Cet requête me donne le résultat évoqué précédemment, à savoir que je n'aurais que les techniciens qui ont au moins une tâche pour le jour J. Or moi je veux tout les technicien, y compris ceux sans taches.

  7. #7
    Membre Expert
    Profil pro
    Inscrit en
    Août 2006
    Messages
    3 277
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2006
    Messages : 3 277
    Par défaut
    Ben le left join est censé justement te renvoyer les techniciens sans tache contrairement au join tout court.

  8. #8
    Membre confirmé
    Homme Profil pro
    Développeur Web
    Inscrit en
    Décembre 2006
    Messages
    126
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : Belgique

    Informations professionnelles :
    Activité : Développeur Web

    Informations forums :
    Inscription : Décembre 2006
    Messages : 126
    Par défaut
    Tout à fait.
    mais le where influence ce comportement.
    Comme je le disais, on peut considéré qu'il s'effectue sur le résultat après le join donc pour faire de manière imagée :

    SELECT tech.*, task.* FROM technicien tech LEFT JOIN task ON task.technician_id = tech.id

    renvoie un résultat du type
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    tech.id | tech.name | task.id | task.date  | task.technician_id |
       11   |    bob    |    1    | 2010-04-23 |         11         |
       11   |    bob    |    2    | 2010-05-21 |         22         |
       22   |   Alice   |    3    | 2010-04-23 |         33         |
       33   |  Franky   |  NULL   |    NULL    |        NULL        |
    Ensuite le where filtre les records.

    WHERE task.date BETWEEN '2010-05-21' AND '2010-05-22'

    Bob: Ok il a une tâche aujourd'hui, il passe donc le filtre avec sa uniquement la tache du jour.
    Alice est rejetée car elle a une tâche mais pas d'aujourd'hui, c'est donc tout le record qui saute. (la date, null, enfreint le between)
    Franky est rejeté car il n'a pas de tâche, les tests sur la date retourne "false" également. Tout le record est éjecté.

    Au final, il ne me reste que Bob.
    (Résultats testé et confirmé)

    Pour obtenir le résultat souhaité en SQL il faut enrichir le "ON" de la clause de jointure :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    select distinct tech.*, tsk.*
    from TECHNICIAN tech 
    left outer join TASK tsk
    	on tech.id=tsk.TECHNICIAN_ID 
    	and tsk.SCHEDULED_START_DATE >= '2009-12-11 00:00:00'
    	and  tsk.SCHEDULED_START_DATE <= '2009-12-11 23:59:59' 
    where tech.PARTNER_ID=1
    Cette requête me donne le résultat attendu.

    Pour continuer, j'ai trouvé comment utiliser un "filter" avec des annotations, c'est d'ailleurs grâce à ce filtre que j'ai obtenu la requête ci-dessus.
    Sur cette page, j'ai trouvé comment déclarer mon filtre via des annotations.
    Et sur cette page, quelques indications supplémentaires pour savoir comment le mettre en œuvre.

    Mais je n'aime pas du tout cette solution.
    1. Elle m'oblige à utiliser des objets "pur hibernate" et plus tout à fait JPA (contrairement à tout ce qui est fait ailleurs dans le projet)
    2. Le plus laid : dans la déclaration de la condition du filtre, il faut mettre le nom de la colonne en base de données et non pas le nom de la propriété de l'entité java.

    Bref, même si ça marche, je suis en quête de mieux

  9. #9
    Membre Expert
    Profil pro
    Inscrit en
    Août 2006
    Messages
    3 277
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2006
    Messages : 3 277
    Par défaut
    Oups désolé, je n'avais pas vu que tu avais déjà proposé le left join dans ta première requête d'où mon intervention.

  10. #10
    Membre éclairé Avatar de VirageGroup
    Profil pro
    Inscrit en
    Décembre 2007
    Messages
    81
    Détails du profil
    Informations personnelles :
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations forums :
    Inscription : Décembre 2007
    Messages : 81
    Par défaut
    bonjour,

    Je propose la requête HQL suivante:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    SELECT tch, tsk  
    FROM Technician tch 
    LEFT OUTER JOIN  tch.tasks tsk 
    WITH tsk.scheduledStartDate >= :schedStart_begin AND tsk.scheduledStartDate <= :schedStart_end 
    WHERE tch.partner=:partner
    order by tch.id
    De cette façon, tous les techniciens sont retrouvés (avec ou sans tâche), ainsi que toutes ses tâches éventuelles.
    Pb: il faut traiter la liste retournée pour réaggreger les tâches à leur technicien et reformer un "couple" : technicien - collection de tâches
    d'où un order by

    VG

  11. #11
    Membre confirmé
    Homme Profil pro
    Développeur Web
    Inscrit en
    Décembre 2006
    Messages
    126
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : Belgique

    Informations professionnelles :
    Activité : Développeur Web

    Informations forums :
    Inscription : Décembre 2006
    Messages : 126
    Par défaut
    Bonjour,

    Merci VirageGroup pour ta réponse.

    C'est une solution similaire que j'ai mise en place.
    Je récupère d'un coté tout mes technicien (triés) dans une liste et les technicien avec leur tâches (uniquement ceux qui en ont) dans une autre.
    Ensuite j'assigne les tâches aux techniciens de la première liste.
    C'est simple à mettre en place et pour le moment ça ne nécessite pas d'être plus optimisé.
    A noter qu'une astuce consiste à bien affecter une liste vide aux technicien qui n'ont pas de tâches sans quoi le lazy-loading peut induire un comportement non souhaité dans mon cas.

    Je flag résolu, même s'il n'y a pas de solution directe visiblement mais uniquement des work-around.

  12. #12
    Candidat au Club
    Profil pro
    Inscrit en
    Avril 2008
    Messages
    3
    Détails du profil
    Informations personnelles :
    Âge : 39
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations forums :
    Inscription : Avril 2008
    Messages : 3
    Par défaut
    Bonjour,
    Je sais que le post est fermé mais pour les lecteurs qui rencontrent le même problème, essayer :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    SELECT tch, tsk  
    FROM Technician tch 
    LEFT OUTER JOIN fetch tch.tasks tsk 
    WHERE tch.partner=:partner
    and (tsk is null or
     tsk.scheduledStartDate >= :schedStart_begin AND tsk.scheduledStartDate <= :schedStart_end )
    order by tch.id

  13. #13
    Membre Expert
    Profil pro
    Inscrit en
    Mars 2002
    Messages
    1 132
    Détails du profil
    Informations personnelles :
    Âge : 53
    Localisation : France

    Informations forums :
    Inscription : Mars 2002
    Messages : 1 132
    Par défaut
    Citation Envoyé par phymbert Voir le message
    Bonjour,
    Je sais que le post est fermé mais pour les lecteurs qui rencontrent le même problème, essayer :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    SELECT tch, tsk  
    FROM Technician tch 
    LEFT OUTER JOIN fetch tch.tasks tsk 
    WHERE tch.partner=:partner
    and (tsk is null or
     tsk.scheduledStartDate >= :schedStart_begin AND tsk.scheduledStartDate <= :schedStart_end )
    order by tch.id
    Attention ce n'est pas la même chose !

    la requête que tu propose ne fonctionne pas EXACTEMENT de la même manière qu'une jointure externe classique.

    En effet, un enregistrement avec une jointure non nulle mais ne respectant pas ton filtre va disparaître alors que l'on aurait voulu l'avoir avec la jointure à null

    Exemple avec ton filtre :
    Code Données sans filtre de date : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    tech.id | tech.name | task.id | task.date  | task.technician_id |
       11   |    bob    |    1    | 2010-04-23 |         11         |
       11   |    bob    |    2    | 2010-05-21 |         22         |
       22   |   Alice   |    3    | 2010-04-23 |         33         |
       33   |  Franky   |  NULL   |    NULL    |        NULL        |

    La jointure classique suivante
    Code SQL : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    SELECT tech.id, tech.name, taks.id, task.date, task.technician_id
    FROM tech 
    LEFT JOIN task ON task.technician_id = tech.id AND task.date > '2010-04-23'
    Donne comme résultat
    Code Données avec filtre de date SQL classique : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    tech.id | tech.name | task.id | task.date  | task.technician_id |
       11   |    bob    |    2    | 2010-05-21 |         22         |
       22   |   Alice   |  NULL   |    NULL    |        NULL        |
       33   |  Franky   |  NULL   |    NULL    |        NULL        |
    Alice a tous ses champs task à NULL

    Alors que la requête HQL
    Code HQL : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    SELECT tech 
    FROM Tech tech 
    LEFT JOIN FETCH tech.tasks 
    WHERE task.date > '2010-04-23'
    Donnera lui comme résultat
    Code Données avec filtre de date en HQL : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    tech.id | tech.name | task.id | task.date  | task.technician_id |
       11   |    bob    |    2    | 2010-05-21 |         22         |
       33   |  Franky   |  NULL   |    NULL    |        NULL        |
    Alice a disparu.

    CQFD

    devyan

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Variable d'une clause where avec quote
    Par kcizth dans le forum Langage SQL
    Réponses: 1
    Dernier message: 17/04/2008, 16h43

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