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 :

Mapper des objets dans Hibernate avec condition sur les objets enfants


Sujet :

Hibernate Java

  1. #1
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Septembre 2005
    Messages
    30
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2005
    Messages : 30
    Points : 25
    Points
    25
    Par défaut Mapper des objets dans Hibernate avec condition sur les objets enfants
    Bonjour à tous.


    j'utilise Hibernate depuis très peu de temps. Au début de mon appli, je cherche à synchroniser l'état de mon appli avec l'état de ma BDD (MySQL en l'occurence). Cependant je souhaiterais ne récupérer que certaines de ces entrées de ma BDD.

    Je m'explique mettons que j'ai les tables suivantes :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     
    CARS
    ----------
    carId
    carName
     
    CARSPASSAGERS
    ----------
    passagerId
    carId
    passagerAge
    et que j'ai les classes suivantes associées :
    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
     
    class Car()
    {
          int id;
          String name;
     
          Set passagers;
    }
     
    class Carpassager()
    {
          Car car;
     
          int id;
          int age;
    }
    j'aimerais avec une requête, récupérer toutes voitures de ma BDD, mais que la classe voiture ne récupère elle que les passagers qui répondent à une condition précise (ex : carpassagerAge > 10). Il ne faut pas que ces passagers soient par contre retirés de la BDD.


    Je sais pas si je suis bien clair. N'hésitez pas à me dire si ce n'est pas le cas.
    Merci d'avance.

  2. #2
    Membre averti
    Profil pro
    Développeur Java
    Inscrit en
    Novembre 2007
    Messages
    301
    Détails du profil
    Informations personnelles :
    Âge : 38
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur Java

    Informations forums :
    Inscription : Novembre 2007
    Messages : 301
    Points : 368
    Points
    368
    Par défaut
    Si j'ai bien compris, tu veux faire une requête qui récupère les voitures dont les passagers ont un age supérieur à 10 mais sans ramener les passagers ?

    Alors tu peux faire, quelque chose comme ça :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    from Car as car where car.passagers.age > 10
    équivalent à
    from Car as car inner join car.passagers as passager where passager.age > 10
    Après à toi de choisir la bonne jointure.

    PS: En faite, Hibernate ne récupère pas l'association par défaut sauf si tu ajoutes le mot clé fetch (inner join fetch, left join fetch) dans ta requête. Tu peux aussi changer ce comportement dans le fichier de mapping pour toujours récupérer une association (en changeant l'attribut fetch).

    Dans ton cas, avec la configuration par défaut dans le fichier de mapping (fetch="select"), Hibernate ne te retournera pas les passagers. Il te donnera un proxy. Et dés que tu appelleras le getter des passagers (si tu es dans une session), il ira faire la sélection.

    Voici le code pour récupérer les voitures ET les passagers :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    from Car as car inner join fetch car.passagers as passager where passager.age > 10

  3. #3
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Septembre 2005
    Messages
    30
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2005
    Messages : 30
    Points : 25
    Points
    25
    Par défaut
    Merci pour ta réponse rapide.

    Ce n'est pas vraiment ce que je souhaite par contre. Je n'ai pas été très clair.

    Ce que je souhaite ce n'est pas "ne pas charger les véhicules qui ont des passagers de moins de 18 ans", mais charger "charger tous les véhicules - qu'ils aient ou non des passagers - mais ne les peupler qu'avec les passagers agés de plus de 18ans". Ca me semble plus simple à comprendre dit comme ça.


    Voilà un exemple concret si mes tables contiennent :
    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
     
    CARS
    -------------------------------------
    carId                          carName
    -------------------------------------
    1                              VoitureRouge
    2                              VoitureBleue
    3                              GrosseMercedez
     
    PASSAGERS (je naffiche pas passagerId qui na aucune importance)
    -------------------------------------
    carId                          passagerAge
    -------------------------------------
    1                              10
    1                              6
    1                              22
    2                              16
    3                              59
    3                              40
    3                              19
    Je souhaite bien que mes 3 voitures soient récupérées (y compris la voiture 2 qui ne contient qu'un mineur), mais que le Set "passagers" de mes voitures ne soit peuplé que de passagers majeurs (concrètement, cela reviendrait à faire une condition sur les sous requêtes écrites par HIBERNATE lors de la récupération des "passagers" lorsque je fais session.createQuery("FROM Car").

    Ici donc voici les sorties que donnerait ces tables :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    > Car (ID 1)
          name = "VoitureRouge"
          passagers =          {22}    /* seul le passager de 22ans est récupéré) */
     
    > Car (ID 2)
          name = "VoitureBleue"
          passagers =          {}    /* vide car le seul passager a moins de 18 */
     
    > Car (ID 3)
          name = "GrosseMercedez"
          passagers =          {59, 40, 19}
    Je précise que mon mapping fait une récupération des associations par défaut visiblement (fetch). Puisque si je fais un createQuery("From Car"), je récupère déjà tous les véhicules, eux même déjà peuplés de mes passagers correspondant.


    Merci encore pour ton coup de main.

  4. #4
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Septembre 2005
    Messages
    30
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2005
    Messages : 30
    Points : 25
    Points
    25
    Par défaut
    Bon, j'ai trouvé un moyen de faire ce que je souhaitais, mais je préfèrerais trouver une autre méthode.

    Je peux faire une condition sur le fetch enfant en rajoutant la clause "where" à mon mapping. Voilà ce que donne le fichier dans l'exemple :
    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
    <class name="Car" table="CARS">
    	<id name="id" type="integer" unsaved-value="null" column="carId">
    		<generator class="increment" />
    	</id>
    	<property name="name" type="string">
    		<column name="carName" length="60" />
    	</property>
    	<list name="passagers" cascade="all-delete-orphan" where="passagerAge>18" inverse="true" lazy="false">
    		<key not-null="true" column="carId" />
    		<list-index column="passagerNum" />
    		<one-to-many class="Carpassager" />
    	</list>
    </class>	
    <class name="Carpassager" table="CARSPASSAGERS">
    	<id name="id" type="integer" unsaved-value="null" column="passagerId">
    		<generator class="increment" />
    	</id>
    	<property name="age" type="string" column="passagerAge" />
    	<many-to-one name="car" class="Car" column="carId" not-null="true" />
    </class>
    Mais je préfèrerais quand même écrire la condition dans le code et non dans le mapping (ce qui empêche toute évolutivité).
    Quelqu'un à une idée ???

  5. #5
    Membre averti
    Profil pro
    Développeur Java
    Inscrit en
    Novembre 2007
    Messages
    301
    Détails du profil
    Informations personnelles :
    Âge : 38
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur Java

    Informations forums :
    Inscription : Novembre 2007
    Messages : 301
    Points : 368
    Points
    368
    Par défaut
    Ce que tu spécifies ici n'est pas un problème relatif à Hibernate mais un problème SQL. Il me semble que c'est possible de l'effectuer en SQL de la manière suivante :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    select *
    from car as car
    left join passager as passager on (passager.carId = car.id) and (passager.passagerAge > 18)
    Après la conversion en HQL je ne la connais pas.

    EDIT : Je viens de trouver

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    from Car as car left join fetch car.passagers as passager with passager.passagerAge > 18

  6. #6
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Septembre 2005
    Messages
    30
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2005
    Messages : 30
    Points : 25
    Points
    25
    Par défaut
    Merci encore darkxan de ta réponse, ceci dit, ça ne correspond pas totalement à ce que j'attends.

    J'avais testé le left join fetch mais le problème est que ça me retourne donc dans le même "result" les cars et les passagers. Or, ce que je cherche ce n'est pas modifier la requête pour la récupérations des cars, mais modifier la requête qu'utilise Car pour la récupération des passagers. Cela correspondrait à modifier une sous-requête.

    J'ai trouvé un moyen de résoudre mon problème en utilisant les filtres de cette manière :
    Hibernate mapping :
    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
    <class name="Car" table="CARS">
    	<id name="id" type="integer" unsaved-value="null" column="carId">
    		<generator class="increment" />
    	</id>
    	<property name="name" type="string">
    		<column name="carName" length="60" />
    	</property>
    	<list name="passagers" cascade="all-delete-orphan" where="passagerAge>18" inverse="true" lazy="false">
    		<key not-null="true" column="carId" />
    		<list-index column="passagerNum" />
    		<one-to-many class="Carpassager" />
     
    		<filter name="OnlyPassagersMajor" condition="passagerAge>18" />
    	</list>
    </class>	
    <class name="Carpassager" table="CARSPASSAGERS">
    	<id name="id" type="integer" unsaved-value="null" column="passagerId">
    		<generator class="increment" />
    	</id>
    	<property name="age" type="string" column="passagerAge" />
    	<many-to-one name="car" class="Car" column="carId" not-null="true" />
    </class>
     
    <filter-def name="OnlyPassagersMajor" />
    et dans Java :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    /* Récupération de ma session */
     
    session.enableFilter("OnlyPassagersMajor");
     
    query = session.createQuery("FROM Car");
     
    /* Ensuite query.list() me permet de récupérer mes cars peuplés de List qui ne contiennent que les passagers de plus de 18 ans */

    Je cherche toujours une meilleure manière de faire ça, surtout, que ça me gêne d'utiliser une requête SQL pour agir sur des propriétés persistantes, je préfèrerais utiliser du HQL du coup. Ceci dit, ça implique d'arriver à faire des "critères" ou "filtres" en Java, sur des sous-requêtes.

  7. #7
    Membre averti
    Profil pro
    Développeur Java
    Inscrit en
    Novembre 2007
    Messages
    301
    Détails du profil
    Informations personnelles :
    Âge : 38
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur Java

    Informations forums :
    Inscription : Novembre 2007
    Messages : 301
    Points : 368
    Points
    368
    Par défaut
    Ce que tu veux me semble contradictoire ou alors je n'ai toujours pas compris. Tu dis préférais utiliser du HQL et vouloir modifier la requête qu'utilise Car pour la récupération des passagers.
    Or, la requête qu'utilise Car pour récupérer les passagers lorsqu'il est nécessaire d'initialiser l'association est construite par Hibernate. Donc tu as deux méthodes possibles, soit celle que j'ai proposé c'est à dire que c'est toi qui fait la jointure soit tu prends ta solution pour obliger hibernate à modifier la jointure mais il n'y a pas de 3ème solution...

    Note : ta requête "from Car" avec ton fichier de configuration actuel (lazy="false") et ton filtre, est équivalent à la requête que j'ai donnée au dessus en HQL. Dans les deux cas, elles récupèrent les voitures ET les passagers qui ont plus de 18 ans.

    Pour ma requête, si tu ne veux pas des passagers, tu peux faire :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    from Car as car left join car.passagers as passager with passager.passagerAge > 18

  8. #8
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Septembre 2005
    Messages
    30
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2005
    Messages : 30
    Points : 25
    Points
    25
    Par défaut
    Merci encore darkxan. Effectivement je n'ai peut être pas été très clair. En tout cas, ton dernier message réponds bien à ma question.

    En fait j'aurais idéalement souhaité trouver un moyen de modifier la sous-requête générée par Hibernate, sans passer par le fichier de mapping, et donc pouvoir le faire en run-time. En gros, générer des filtres en run-time.

    Le problème de la requête LEFT JOIN étant qu'elle me renvoit (et c'est logique) aussi les passagers (ceux de plus de 18 ans), or je souhaitais conserver le comportement de Hibernate qui s'occupe tout seul de peupler les voitures avec les passagers. Et le LEFT JOIN FETCH renvoie des erreurs (impossible to use FETCH and WITH together) et évidemment si je remplace le WITH par un WHERE cela n'a plus le même sens. LEFT JOIN ne correspond de toutes les manières pas à mes besoins, puisque cela "réuni" les données d'une voiture et d'un passager sur la même ligne, ce qui double les données renvoyées par la requête.


    Bref, dans tous les cas, les filtres correspondent à ce que je veux et d'après ce que tu dis il n'y a pas d'autres solutions donc ça me suffira. Même si à l'idéal j'aimerais pouvoir en générer en run-time, mais je vois pas comment puisque apparemment il faut obligatoirement qu'ils soient inscrit dans le fichier de mapping (il me semble).


    Merci pour l'aide.

  9. #9
    Membre averti
    Profil pro
    Développeur Java
    Inscrit en
    Novembre 2007
    Messages
    301
    Détails du profil
    Informations personnelles :
    Âge : 38
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur Java

    Informations forums :
    Inscription : Novembre 2007
    Messages : 301
    Points : 368
    Points
    368
    Par défaut
    Tu as toujours la méthode : createFilter(Object collection, String queryString). Je pense que cela pourrait répondre à ton envie de faire le filtre dans ton code Java.

    Rien ne t'empêche aussi de mettre un paramètre dans ton filtre qui se trouve dans le fichier de mapping et de faire un setParameter("age", 18);.

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

Discussions similaires

  1. If avec condition sur meme objet
    Par jazzybluesy dans le forum Macros et VBA Excel
    Réponses: 2
    Dernier message: 03/10/2012, 14h50
  2. Réponses: 3
    Dernier message: 06/06/2011, 12h56
  3. Réponses: 0
    Dernier message: 27/09/2008, 12h45
  4. [XPath] test d'existence de noeud avec conditions sur les enfants
    Par MasterOfChakhaL dans le forum XSL/XSLT/XPATH
    Réponses: 6
    Dernier message: 11/04/2007, 10h42
  5. [XSLT]copie partielle avec condition sur les axes
    Par MasterOfChakhaL dans le forum XSL/XSLT/XPATH
    Réponses: 5
    Dernier message: 13/10/2006, 19h15

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