Que contrôle exactement la stratégie de récupération de JPA? Je ne peux détecter aucune différence entre impatient et paresseux. Dans les deux cas, JPA / Hibernate ne joint pas automatiquement les relations plusieurs-à-un.
Exemple: la personne a une seule adresse. Une adresse peut appartenir à plusieurs personnes. Les classes d'entités annotées JPA ressemblent à:
@Entity
public class Person {
@Id
public Integer id;
public String name;
@ManyToOne(fetch=FetchType.LAZY or EAGER)
public Address address;
}
@Entity
public class Address {
@Id
public Integer id;
public String name;
}
Si j'utilise la requête JPA:
select p from Person p where ...
JPA / Hibernate génère une requête SQL à sélectionner dans la table Person, puis une requête d'adresse distincte pour chaque personne:
select ... from Person where ...
select ... from Address where id=1
select ... from Address where id=2
select ... from Address where id=3
C'est très mauvais pour les grands ensembles de résultats. S'il y a 1000 personnes, il génère 1001 requêtes (1 de Person et 1000 distinctes de Address). Je le sais parce que je regarde le journal des requêtes de MySQL. J'ai cru comprendre que la définition du type de récupération d'adresse sur impatient entraînera JPA / Hibernate à interroger automatiquement avec une jointure. Cependant, quel que soit le type d'extraction, il génère toujours des requêtes distinctes pour les relations.
Ce n'est que lorsque je lui dis explicitement de se joindre qu'il rejoint réellement:
select p, a from Person p left join p.address a where ...
Est-ce que j'ai râté quelque chose? Je dois maintenant coder chaque requête pour qu'elle rejoigne les relations plusieurs-à-un. J'utilise l'implémentation JPA d'Hibernate avec MySQL.
Edit: Il apparaît (voir FAQ Hibernate ici et ici ) qui FetchType
n'a pas d'impact sur les requêtes JPA. Donc, dans mon cas, je lui ai explicitement dit de se joindre.
Réponses:
JPA ne fournit aucune spécification sur les annotations de mappage pour sélectionner la stratégie de récupération. En général, les entités associées peuvent être récupérées de l'une des manières indiquées ci-dessous
Donc
SELECT
etJOIN
sont deux extrêmes et seSUBSELECT
situe entre les deux. On peut choisir une stratégie appropriée en fonction de son modèle de domaine.Par défaut, il
SELECT
est utilisé à la fois par JPA / EclipseLink et Hibernate. Cela peut être remplacé en utilisant:dans Hibernate. Il permet également de définir le
SELECT
mode explicitement en utilisant@Fetch(FetchMode.SELECT)
ce qui peut être réglé en utilisant la taille du lot, par exemple@BatchSize(size=10)
.Les annotations correspondantes dans EclipseLink sont:
la source
"mxc" a raison.
fetchType
spécifie simplement quand la relation doit être résolue.Pour optimiser le chargement hâtif en utilisant une jointure externe, vous devez ajouter
à votre domaine. Il s'agit d'une annotation spécifique à l'hibernation.
la source
L'attribut fetchType contrôle si le champ annoté est extrait immédiatement lorsque l'entité principale est extraite. Cela ne dicte pas nécessairement la manière dont l'instruction fetch est construite, l'implémentation réelle de sql dépend du fournisseur que vous utilisez toplink / hibernate, etc.
Si vous définissez
fetchType=EAGER
Cela signifie que le champ annoté est rempli avec ses valeurs en même temps que les autres champs de l'entité. Ainsi, si vous ouvrez un entitymanager récupérez vos objets person, puis fermez le entitymanager, le fait de faire par la suite un person.address n'entraînera pas la levée d'une exception de chargement différé.Si vous définissez,
fetchType=LAZY
le champ n'est renseigné que lors de l'accès. Si vous avez fermé le gestionnaire d'entités d'ici là, une exception de chargement différé sera levée si vous faites une personne.adresse. Pour charger le champ, vous devez remettre l'entité dans un contexte entitymangers avec em.merge (), puis effectuez l'accès au champ, puis fermez le gestionnaire d'entités.Vous pouvez souhaiter un chargement différé lors de la construction d'une classe de clients avec une collection pour les commandes des clients. Si vous avez récupéré chaque commande d'un client lorsque vous vouliez obtenir une liste de clients, cela peut être une opération de base de données coûteuse lorsque vous recherchez uniquement le nom du client et les coordonnées. Il est préférable de laisser l'accès à la base de données plus tard.
Pour la deuxième partie de la question - comment mettre en veille prolongée pour générer un SQL optimisé?
Hibernate devrait vous permettre de fournir des conseils sur la façon de construire la requête la plus efficace, mais je soupçonne qu'il y a un problème avec la construction de votre table. La relation est-elle établie dans les tableaux? Hibernate a peut-être décidé qu'une simple requête serait plus rapide qu'une jointure, surtout s'il manque des index, etc.
la source
Essayez avec:
Cela fonctionne pour moi de manière similaire avec JPA2 / EclipseLink, mais il semble que cette fonctionnalité soit également présente dans JPA1 :
la source
Si vous utilisez EclipseLink au lieu d'Hibernate, vous pouvez optimiser vos requêtes par des "conseils de requête". Consultez cet article du Wiki Eclipse: EclipseLink / Exemples / JPA / QueryOptimization .
Il y a un chapitre sur la «lecture jointe».
la source
pour rejoindre, vous pouvez faire plusieurs choses (en utilisant eclipselink)
en jpql, vous pouvez effectuer une récupération de jointure gauche
dans la requête nommée, vous pouvez spécifier une indication de requête
dans TypedQuery, vous pouvez dire quelque chose comme
query.setHint("eclipselink.join-fetch", "e.projects.milestones");
il y a aussi un indice de récupération par lots
query.setHint("eclipselink.batch", "e.address");
voir
http://java-persistence-performance.blogspot.com/2010/08/batch-fetching-optimizing-object-graph.html
la source
J'ai eu exactement ce problème à l'exception du fait que la classe Person avait une classe de clé intégrée. Ma propre solution était de les rejoindre dans la requête ET de supprimer
@Fetch(FetchMode.JOIN)
Ma classe d'identifiant intégrée:
la source
Deux choses me viennent à l'esprit.
Tout d'abord, êtes-vous sûr de vouloir dire ManyToOne pour adresse? Cela signifie que plusieurs personnes auront la même adresse. S'il est modifié pour l'un d'entre eux, il sera modifié pour tous. Est-ce votre intention? 99% du temps, les adresses sont «privées» (en ce sens qu'elles n'appartiennent qu'à une seule personne).
Deuxièmement, avez-vous d'autres relations enthousiastes sur l'entité Personne? Si je me souviens bien, Hibernate ne peut gérer qu'une seule relation enthousiaste sur une entité, mais ce sont peut-être des informations obsolètes.
Je dis cela parce que votre compréhension de la façon dont cela devrait fonctionner est essentiellement correcte à partir de là où je suis assis.
la source