9
votes

Hibernate noob fetch

J'ai deux classes, test2 et test3. Test2 a un attribut test3 qui est une instance de test3. En d'autres termes, j'ai une association unidirectionnelle de l'onetoone, avec Test2 ayant une référence à Test3.

Lorsque je sélectionne Test2 à partir de la base de données, je peux voir qu'une sélection distincte est mise en œuvre pour obtenir les détails de la classe Test3 associée. C'est le célèbre problème 1 + N Sélectionnements. P>

Pour résoudre ce problème pour utiliser une seule sélection, j'essaie d'utiliser la fetch = Rejoindre Annotation, que je comprends être @fetch (fetchmode.join)

Cependant, avec la récupération définie pour rejoindre, je vois toujours des sélections séparées. Voici les parties pertinentes de ma configuration .. p>

hibernate.cfg.xml: p> xxx pré>

test2: p> xxx Pré>

NB I Définissez le fetchtype à la désespoir de désespoir, même s'il est par défaut de faire défaut de toute façon désireuse pour les mappages d'onsetone, mais cela n'a fait aucune différence. P>

Merci pour toute aide! P> Edit: J'ai à peu près abandonné l'essai d'utiliser FetchMode.join - Peut-on confirmer qu'ils l'ont fait fonctionner à savoir produire une jointure extérieure gauche? Dans les docs, je vois que "d'habitude, le document de mappage n'est pas utilisé pour personnaliser la récupération. Au lieu de cela, nous gardons le comportement par défaut et la remplace pour une transaction particulière, en utilisant la jointure gauche récupération dans HQL" P>

si Je fais une jointure gauche Fetch à la place: p>

Query = Session.Creatiequier ("de Test2 T2 Gauche Rejoignez Fetch T2.Test3"); P>

Alors je reçois effectivement les résultats que je veux - c'est-à-dire une jointure extérieure gauche dans la requête. P>

Modifier le numéro 2: P>

gars, merci beaucoup pour vos réponses. Maintenant, je veux aller au bas de cela. Je trouve généralement que lorsque j'échangeais quelque chose, je finais à en apprendre beaucoup plus que je ne le pensais. P>

Une chose que j'ai déjà apprise - je courais sur de vieilles bâtiments d'hibernate parce que je n'ai pas Réalisez que le référentiel Maven était obsolète. Maintenant, je suis également accroché au référentiel JBoss et j'ai les dernières versions des annotations hibernate et hibernate - 3.5.1-finale dans les deux cas. P>

J'ai mis en place un petit cas de test qui le simplifie autant que je peux - je vois toujours le problème en 3.5.1-final, ce que je suis à 99%, c'est certain que c'est juste quelque chose de stupide, je ne suis pas en place, surtout Ross, étant donné que vous l'avez eu travailler (merci d'avoir pris le temps de l'essayer par la voie) p>

donc j'ai ces classes (texte intégral cette fois-ci) p>

Classe A P>

    /* generates the left outer join
    A a = (A)session.load(A.class,1);
    System.out.println(a.getId()+" = "+a.getB().getName());
    */

    // Creates separate selects for each object b associated with each a
    Query query = session.createQuery("from A");
    List results = query.list();
    A a = (A)results.get(0);
    System.out.println(a.getId()+" = "+a.getB().getName());


3 commentaires

Juste une pensée: je vois que vos annotations sont sur le getter - est-ce la même chose pour tous les autres membres de Test2? Sinon, la fixation, cela peut résoudre votre problème.


Merci Peter. Oui, ils sont tous sur les getters (mais j'ai fait cette erreur dans le passé et ça fait des choses.)


Malheureusement, la itérate ne fonctionne pas - en fait c'est pire! Maintenant, il fait le choix initial, qui lui donne l'ID d'objet, puis une sélection de chaque objet associé, mais faisant une jointure extérieure gauche via la table d'association, plutôt que d'aller directement à l'Associatee - une sélection séparée plus compliquée! (Voir mon commentaire sur votre réponse ci-dessous) Oh bien. Je pense que ça va à HQL d'être en sécurité, ce qui est peut-être meilleur car il est plus explicite ..


3 Réponses :


2
votes

J'ai créé une application très simple pour tester le scénario que vous obtenez et que votre code devrait fonctionner (cela fonctionne pour moi). La seule chose que j'ai essayée qui me donnera plusieurs instructions sélectionnées est de régler max_fetch_depth sur 0. Si défini sur 2 (ou non configuré), je reçois la jointure extérieure gauche dans ma requête. Quelle version d'hibernate utilisez-vous? J'utilise 3.4.0.ga.

Edit: Vous trouverez ci-dessous la simple application que j'ai utilisée (avec les mêmes versions mentionnées par Pascal): P>

CFG: P>

Hibernate: 
    select
        phone_.id,
        phone_.number as number1_ 
    from
        phone phone_ 
    where
        phone_.id=?
Hibernate: 
    insert 
    into
        phone
        (number, id) 
    values
        (?, ?)
Hibernate: 
    insert 
    into
        person
        (name, phone_id, id) 
    values
        (?, ?, ?)
Hibernate: 
    select
        person0_.id as id0_1_,
        person0_.name as name0_1_,
        person0_.phone_id as phone3_0_1_,
        phone1_.id as id1_0_,
        phone1_.number as number1_0_ 
    from
        person person0_ 
    left outer join
        phone phone1_ 
            on person0_.phone_id=phone1_.id 
    where
        person0_.id=?
1234567


5 commentaires

Merci d'avoir pris le temps de tester cela, Ross. Je l'apprécie vraiment .. J'ai collé dans le code complet d'un test plus petit. Je serais vraiment intéressé si cela fonctionne correctement dans votre configuration. Cela ne fonctionne pas dans le mien, même moins de 3,5,1-finale, mais cela peut être quelque chose dans mon code, alors j'ai passé tout cela .. Merci :-)


Ok - nous sommes sur la bonne voie. Si je fais une charge, comme vous le faites, je reçois le comportement de jointure extérieur gauche. Cependant, si je fais la liste (), puis obtenez le premier objet de la liste, il suffit de sélectionner - voir mon numéro de modification 3 dans la question initiale.


Oui je reçois les mêmes résultats que vous. Au lieu d'utiliser la liste, essayez d'utiliser itérer - j'ai donné cela un essai et semblait fonctionner


Merci ross. Je ne suis pas sûr que itérer est une alternative viable: des Javadocs: «Les entités sont revenues comme des résultats sont initialisées à la demande. La première requête SQL renvoie uniquement des identificateurs». Cela suggère que le scénario 1 + N se produira toujours, juste retardé lorsque vous allez utiliser les objets, plutôt que lorsque l'appel initial est effectué? Si je comprends bien, la liste n'est pas à la demande, mais plutôt tout en un. Je pense que la réponse va simplement être une kidelle plutôt kludgy, y compris des jointures dans le HQL. Peut-être que c'est mieux qu'il soit explicite comme ça de toute façon ..


J'ai essayé d'utiliser l'interface de critères pour obtenir la liste, au lieu d'utiliser la requête Hibernate. Cela semblait travailler. Je vais éditer le code ci-dessus à des fins de référence.



4
votes

Lorsque je sélectionne Test2 à partir de la base de données, je peux voir qu'une sélection distincte est mise en œuvre pour obtenir les détails de la classe Test3 associée.

Je suis très intéressé par le code de l'autre réponse parce que c'est ce que je vois aussi lorsque vous testez le code que vous affichez, il génère deux sélectionneurs pour un à partir de test2 .

J'utilise les dépendances suivantes:

  • org.hibernate: hibernate-entitymanager: JAR: 3.4.0.GA: Compiler
  • org.hibernate: EJB3-Persistence: JAR: 1.0.2.GA: Compiler
  • org.hibernate: hibernate-annotations: JAR: 3.1.0.GA: Compiler
  • org.hibernate: hibernate-annotations: JAR: 3.4.0.GA: Compiler
  • org.hibernate: hibernate-noyau: JAR: 3.3.0.Sp1: Compiler

    Je fixe le fetchtype à l'impression de désespéré, même s'il est par défaut de toute façon impatiente pour des mappages d'OneOne, mais cela n'a fait aucune différence.

    Cela n'a aucun impact si vous utilisez des annotations hibernées car les annotations Hibernate remplacent les options d'extraction EJB3. Voir 2.4.5.1. Options paresseuses et récupérer des modes .


2 commentaires

Merci d'avoir pris le temps, Pascal :-) C'est bien d'obtenir une réplication. J'ai simplifié l'étui de test ci-dessus. Je suppose que l'une ou l'autre, il y a une erreur dans ma configuration que quelqu'un va repérer, ou il y a quelque chose de drôle dans cette situation, mais pas dans celui que Ross présente.


J'ai accepté votre réponse aussi correcte, en partie pour tester le problème (que Ross a également fait, et je marquerais aussi sa réponse aussi si je pouvais), mais aussi parce que je me le reproduisais, vous m'avez empêché de passer insensé :-)



17
votes

comme distillation:

@fetch (jointure) sera ignoré si vous utilisez l'interface de requête (E.G.: Session.Createequery ()) mais il sera correctement utilisé si vous utilisez l'interface de critères.

C'est pratiquement un bug dans l'hibernate qui n'a jamais été résolu. Il est regrettable car beaucoup d'applications utilisent l'interface de requête et ne peuvent pas être facilement migrées sur l'interface de critères.

Si vous utilisez l'interface de requête, vous devez toujours ajouter des instructions religieuses dans le HQL manuellement.


3 commentaires

Je mourais de savoir pourquoi cela se passe et cela n'a été mentionné nulle part. Merci Monsieur. Mon entreprise et moi-même vous devez une grosse affaire.


@Eyadebrahim en fait la documentation est clair à ce sujet: "représente une stratégie d'approbation de l'association. Ceci est utilisé avec l'API de critères pour spécifier des stratégies d'extraction d'exécution. Pour les requêtes HQL, utilisez le mot-clé Fetch à la place." href = "http://docs.jboss.org/ibernate/core/3.3/api/org/hibernate/fetchmode.html" rel = "nofollow Noreferrer"> docs.jboss.org/hibernate/core/3.3/api/ org / hibernate / ...


Ne fonctionne pas pour moi si vous utilisez des critères API ou JPQL. J'utilise Hibernate 4.2.15.