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 :

N+1 requêtes sur un ManyToOne


Sujet :

JPA Java

  1. #1
    Membre averti
    Avatar de rozwel
    Inscrit en
    Mars 2002
    Messages
    324
    Détails du profil
    Informations forums :
    Inscription : Mars 2002
    Messages : 324
    Points : 334
    Points
    334
    Par défaut N+1 requêtes sur un ManyToOne
    Je débute avec JPA mais j'utilise Hibernate depuis longtemps. Là j'ai un souci d'optimisation des selects.
    J'ai les 2 classes suivantes:

    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
    public class Bill {
        @Id
        @GeneratedValue
        private long id;
        private long totalBytes;
        private long uploadedBytes;
        private String fileName;
        private Date billingDate;
        private Date importDate;
        @ManyToOne
        @Fetch(FetchMode.JOIN)    
        private Customer customer;
        @ManyToOne
        private Format format;
        private String filePathOnServer;
    }
     
    @Entity
    public class Customer {
        @Id
        @GeneratedValue
        private long id;
        private String name;
        private String number;
        private Address postAddress;
        @OneToMany
        private Set<Bill> bills = new HashSet<Bill>();
    }
    Ensuite j'ai un DAO JPA qui me sert notamment à récupérer tous les bills:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
      public List<Bill> findAll() {
            return entityManager.createQuery("select b from Bill b").getResultList();
        }
    Et avant même que j'appelle getCustomer() sur bill, Hibernate a déjà exécuté N+1 requêtes: 1 pour les bills, et N pour le customer de chaque bill. Or, je me serais attendu à ce qu'il n'exécute qu'une seule requête avec un join.

    Est-ce qu'il n'est pas censé faire ça par défaut? Sinon comment je peux forcer Hibernate à le faire?

    Je précise que je suis sur une base MySQL 5.
    Sébastien ARBOGAST
    SCJP

  2. #2
    Membre régulier
    Profil pro
    Inscrit en
    Mars 2007
    Messages
    185
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2007
    Messages : 185
    Points : 109
    Points
    109
    Par défaut
    salut,

    tu dois faire comme cela dans la classe Customer pour avoir une relation bidirectionelle entre les deux entities

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
     @OneToMany(mappedBy="customer")
        private Set<Bill> bills = new HashSet<Bill>();
    comme cela tu auras qu'un clé etranger dans la table Bill et pas les deux (cle de jointure + table de jointure bill_costumer)

    essaie cela et normalement JPA te fera qu'une seule requete avec un left outer join...
    A+

  3. #3
    Membre régulier
    Profil pro
    Inscrit en
    Mars 2007
    Messages
    185
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2007
    Messages : 185
    Points : 109
    Points
    109
    Par défaut
    juste un truc dans le cas d'une relation ManyToOne bidirectionnelle la collection la plus performante et Collection avec un @IdCollection et pas dans ton cas le Set...

    A+

  4. #4
    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
    Citation Envoyé par rozwel Voir le message
    Et avant même que j'appelle getCustomer() sur bill, Hibernate a déjà exécuté N+1 requêtes: 1 pour les bills, et N pour le customer de chaque bill. Or, je me serais attendu à ce qu'il n'exécute qu'une seule requête avec un join.

    Est-ce qu'il n'est pas censé faire ça par défaut? Sinon comment je peux forcer Hibernate à le faire?
    Regarde du côté de FetchType.LAZY
    N'oubliez pas de consulter les FAQ Java et les cours et tutoriels Java

  5. #5
    Membre averti
    Avatar de rozwel
    Inscrit en
    Mars 2002
    Messages
    324
    Détails du profil
    Informations forums :
    Inscription : Mars 2002
    Messages : 324
    Points : 334
    Points
    334
    Par défaut
    Je ne pense pas: FetchType.LAZY décalera la requête sur customer à quand je fais l'appel à bill.getCustomer().

    Par contre j'ai essayé de modifier les annotations pour améliorer les choses:
    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
     
    @Entity
    public class Bill {
        @Id
        @GeneratedValue
        private long id;
        private long totalBytes;
        private long uploadedBytes;
        private String fileName;
        private Date billingDate;
        private Date importDate;
        @ManyToOne
        private Customer customer;
        @ManyToOne
        private Format format;
        private String filePathOnServer;
    }
     
    @Entity
    public class Customer {
        @Id
        @GeneratedValue
        private long id;
        private String name;
        private String number;
        private Address postAddress;
        @OneToMany(mappedBy = "customer")
        private Collection<Bill> bills = new HashSet<Bill>();
    }
    Effectivement, la table de mapping a disparu, par contre mon log me dit toujours que la requête sur Customer est séparée.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    Hibernate: select bill0_.id as id2_, bill0_.billingDate as billingD2_2_, bill0_.customer_id as customer8_2_, bill0_.fileName as fileName2_, bill0_.filePathOnServer as filePath4_2_, bill0_.format_id as format9_2_, bill0_.importDate as importDate2_, bill0_.totalBytes as totalBytes2_, bill0_.uploadedBytes as uploaded7_2_ from Bill bill0_ where 1=1 order by bill0_.id
    Hibernate: select customer0_.id as id0_0_, customer0_.name as name0_0_, customer0_.number as number0_0_, customer0_.addressLine1 as addressL4_0_0_, customer0_.addressLine2 as addressL5_0_0_, customer0_.city as city0_0_, customer0_.postCode as postCode0_0_ from Customer customer0_ where customer0_.id=?
    Sébastien ARBOGAST
    SCJP

  6. #6
    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
    Si tu veux qu'il fasse la jonction directement dans la requête initiale sur Bill, il faudrait utiliser "fetch join" dans la requête, dans ce genre
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    entityManager.createQuery("select b from Bill b fetch join customer ...")
    N'oubliez pas de consulter les FAQ Java et les cours et tutoriels Java

  7. #7
    Membre averti
    Avatar de rozwel
    Inscrit en
    Mars 2002
    Messages
    324
    Détails du profil
    Informations forums :
    Inscription : Mars 2002
    Messages : 324
    Points : 334
    Points
    334
    Par défaut
    Merci beaucoup, celle là a marché:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    select b from Bill b join fetch b.customer
    Je ne comprends toujours pas pourquoi @Fetch(FetchMode.JOIN) ne fait pas ça automatiquement, mais au moins j'ai quelque chose qui fonctionne maintenant.

    Merci.
    Sébastien ARBOGAST
    SCJP

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

Discussions similaires

  1. [ADO] Requète sur dates
    Par cdlr27 dans le forum Bases de données
    Réponses: 3
    Dernier message: 26/01/2005, 22h39
  2. Requête sur un MemData
    Par claude dans le forum Bases de données
    Réponses: 5
    Dernier message: 23/12/2004, 10h11
  3. Requête sur date
    Par guenfood dans le forum Access
    Réponses: 11
    Dernier message: 08/12/2004, 16h11
  4. Requête sur un serveur lié
    Par Guizz dans le forum MS SQL Server
    Réponses: 2
    Dernier message: 06/08/2003, 11h35
  5. requête sur l'année d'une date
    Par jo77 dans le forum Langage SQL
    Réponses: 4
    Dernier message: 30/07/2003, 09h28

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