Quelle est la différence entre JOIN et JOIN FETCH lors de l'utilisation de JPA et Hibernate

183

S'il vous plaît, aidez-moi à comprendre où utiliser un JOIN régulier et où un JOIN FETCH.

Par exemple, si nous avons ces deux requêtes

FROM Employee emp
JOIN emp.department dep

et

FROM Employee emp
JOIN FETCH emp.department dep

Y a-t-il une différence entre eux? Si oui, lequel utiliser quand?

Abbas
la source
2
vous pouvez le trouver ici lien lu 14.3. Associations et jointures
Angga
4
J'ai parcouru cette documentation mais je ne sais toujours pas où dois-je utiliser un JOIN et où un JOIN FETCH.
abbas
2
Si le mappage @oneToOne est défini sur FetchType.LAZY et que vous utilisez la deuxième requête (car vous avez besoin que les objets Department soient chargés dans le cadre des objets Employee), Hibernate émettra des requêtes pour récupérer des objets Department pour chaque objet Employee individuel il récupère de DB. Plus tard dans le code, vous pourrez accéder aux objets Department via une association à valeur unique Employé-Département et Hibernate n'émettra aucune requête pour récupérer l'objet Service pour l'employé donné. N'oubliez pas qu'Hibernate émet toujours des requêtes égales au nombre d'employés qu'il a récupérés.
Bunti
Pour aider à la chasse aux documents ~ Fetching Strategies
Eddie B
1
@ShameeraAnuranga Je pense que dans ce cas, vous aurez besoin d'une JOINTURE EXTERNE GAUCHE.
abbas

Réponses:

180

Dans ces deux requêtes, vous utilisez JOIN pour interroger tous les employés auxquels au moins un service est associé.

Mais la différence est la suivante: dans la première requête, vous ne renvoyez que les employés pour la mise en veille prolongée. Dans la deuxième requête, vous renvoyez les employés et tous les départements associés.

Ainsi, si vous utilisez la deuxième requête, vous n'aurez pas besoin de faire une nouvelle requête pour accéder à nouveau à la base de données pour voir les départements de chaque employé.

Vous pouvez utiliser la deuxième requête lorsque vous êtes sûr que vous aurez besoin du département de chaque employé. Si vous n'avez pas besoin du département, utilisez la première requête.

Je recommande de lire ce lien si vous avez besoin d'appliquer une condition WHERE (ce dont vous aurez probablement besoin): Comment exprimer correctement JPQL "join fetch" avec la clause "where" en tant que JPA 2 CriteriaQuery?

Mettre à jour

Si vous n'utilisez pas fetchet que les départements continuent d'être retournés, c'est parce que votre mappage entre l'employé et le département (a @OneToMany) est réglé avec FetchType.EAGER. Dans ce cas, toute fetchrequête HQL (avec ou non) avec FROM Employeeapportera tous les départements. N'oubliez pas que tous les mappages * ToOne ( @ManyToOneet @OneToOne) sont EAGER par défaut.

Dherik
la source
1
Quel sera le comportement si nous exécutons l'instruction sans extraire et obtenir le résultat. Alors dans la session nous traiterons au département?
gstackoverflow
1
@gstackoverflow, yes
Dherik
J'utilise la requête native avec Lazy Fetch dans les deux côtés de la relation mais charge toujours la hiérarchie des relations enfants.
Badamchi
Il fetchconvient de mentionner qu'il doit être utilisé si (en utilisant notre exemple) vous souhaitez commander par un attribut de service. Sinon, (valable au moins pour PG), vous pourriez obtenirERROR: for SELECT DISTINCT, ORDER BY expressions must appear in select list
long le
60

dans ce lien que j'ai mentionné précédemment dans le commentaire, lisez cette partie:

Une jointure «fetch» ​​permet d'initialiser des associations ou des collections de valeurs avec leurs objets parents en une seule sélection. Ceci est particulièrement utile dans le cas d'une collection. Il remplace efficacement la jointure externe et les déclarations différées du fichier de mappage pour les associations et les collections.

ce "JOIN FETCH" aura son effet si vous avez la propriété (fetch = FetchType.LAZY) pour une collection à l'intérieur d'une entité (exemple ci-dessous).

Et ce n'est que l'effet de la méthode "quand la requête doit se produire". Et vous devez également savoir ceci :

hibernate a deux notions orthogonales: quand l'association est-elle récupérée et comment est-elle récupérée. Il est important de ne pas les confondre. Nous utilisons fetch pour régler les performances. Nous pouvons utiliser lazy pour définir un contrat pour quelles données sont toujours disponibles dans toute instance détachée d'une classe particulière.

quand l'association est-elle récupérée -> votre type "FETCH"

comment est-il récupéré -> Rejoindre / sélectionner / Sous-sélectionner / Lot

Dans votre cas, FETCH n'aura son effet que si vous avez un département comme ensemble dans Employee, quelque chose comme ceci dans l'entité:

@OneToMany(fetch = FetchType.LAZY)
private Set<Department> department;

quand vous utilisez

FROM Employee emp
JOIN FETCH emp.department dep

vous obtiendrez empet emp.dep. quand vous n'avez pas utilisé fetch, vous pouvez toujours obtenir emp.depmais hibernate traitera une autre sélection dans la base de données pour obtenir cet ensemble de services.

donc c'est juste une question de réglage des performances, à propos de vous voulez obtenir tous les résultats (vous en avez besoin ou non) en une seule requête (récupération impatiente), ou vous voulez l'interroger plus tard lorsque vous en avez besoin (récupération paresseuse).

Utilisez la récupération rapide lorsque vous avez besoin d'obtenir de petites données avec une sélection (une grande requête). Ou utilisez la récupération paresseuse pour interroger ce dont vous avez besoin plus tard (plusieurs requêtes plus petites).

utilisez fetch quand:

  • pas de grande collection / ensemble inutile à l' intérieur de cette entité que vous êtes sur le point d'obtenir

  • la communication entre le serveur d'applications et le serveur de base de données est trop longue et nécessite beaucoup de temps

  • vous pouvez avoir besoin de cette collection plus tard lorsque vous n'y avez pas accès (en dehors de la méthode / classe transactionnelle )

Angga
la source
Pourriez-vous l'expliquer pour les requêtes que je viens d'écrire dans la question mise à jour.
abbas
considération utile: "pas de grande collection / ensemble inutile à l'intérieur de cette entité que vous êtes sur le point d'obtenir"
Divs
Les départements seraient-ils toujours recherchés avec impatience si les départements à l'intérieur de l'employé étaient un Listau lieu d'un Set?
Stephane
L'utilisation du FETCHmot - clé dans une instruction JPQL implique-t-elle une propriété rapidement récupérée?
Stephane
15

JOINDRE

Lors de l'utilisation JOINcontre des associations d'entité, JPA générera un JOIN entre l'entité parente et les tables d'entité enfant dans l'instruction SQL générée.

Donc, en prenant votre exemple, lors de l'exécution de cette requête JPQL:

FROM Employee emp
JOIN emp.department dep

Hibernate va générer l'instruction SQL suivante:

SELECT emp.*
FROM employee emp
JOIN department dep ON emp.department_id = dep.id

Notez que la SELECTclause SQL contient uniquement les employeecolonnes de la table, et non departmentcelles. Pour récupérer les departmentcolonnes de la table, nous devons utiliser à la JOIN FETCHplace de JOIN.

REJOINDRE FETCH

Ainsi, par rapport à JOIN, le JOIN FETCHvous permet de projeter les colonnes de la table de jointure dans la SELECTclause de l'instruction SQL générée.

Ainsi, dans votre exemple, lors de l'exécution de cette requête JPQL:

FROM Employee emp
JOIN FETCH emp.department dep

Hibernate va générer l'instruction SQL suivante:

SELECT emp.*, dept.*
FROM employee emp
JOIN department dep ON emp.department_id = dep.id

Notez que, cette fois, les departmentcolonnes de la table sont également sélectionnées, pas seulement celles associées à l'entité répertoriée dans la FROMclause JPQL.

C'est également JOIN FETCHun excellent moyen de résoudre le problème LazyInitializationExceptionlors de l'utilisation d'Hibernate, car vous pouvez initialiser les associations d'entités à l'aide de la FetchType.LAZYstratégie de récupération avec l'entité principale que vous récupérez.

Vlad Mihalcea
la source
Est-il possible d'utiliser plusieurs JOIN FETCH dans la même requête?
A.Onur Özcan le
2
Vous pouvez JOIN FETCH plusieurs associations plusieurs-à-un et un-à-un et au plus une collection. La récupération de plusieurs collections, comme des associations un-à-plusieurs ou plusieurs-à-plusieurs, aboutira à un produit cartésien . Toutefois, si vous souhaitez récupérer plusieurs collections, vous pouvez utiliser des requêtes secondaires pour les deuxième, troisième, ..., nième collections. Consultez cet article pour plus de détails.
Vlad Mihalcea le
5

Si le @oneToOnemappage est défini sur FetchType.LAZYet que vous utilisez une deuxième requête (car vous avez besoin que les objets Department soient chargés dans le cadre des objets Employee), Hibernate émettra des requêtes pour récupérer des objets Department pour chaque objet Employee qu'il récupère dans la base de données.

Plus tard, dans le code, vous pourrez accéder aux objets Department via une association à valeur unique Employé-Département et Hibernate n'émettra aucune requête pour récupérer l'objet Service pour l'employé donné.

N'oubliez pas qu'Hibernate émet toujours des requêtes égales au nombre d'employés qu'il a récupérés. Hibernate émettra le même nombre de requêtes dans les deux requêtes ci-dessus, si vous souhaitez accéder aux objets Department de tous les objets Employee

Bunti
la source
2

Dherik: Je ne suis pas sûr de ce que vous dites, lorsque vous n'utilisez pas de récupération, le résultat sera de type: List<Object[ ]>ce qui signifie une liste de tables d'objets et non une liste d'employés.

Object[0] refers an Employee entity 
Object[1] refers a Departement entity 

Lorsque vous utilisez fetch, il n'y a qu'une seule sélection et le résultat est la liste des employés List<Employee>contenant la liste des départements. Il remplace la déclaration paresseuse de l'entité.

Bilal BBB
la source
Je ne sais pas si je comprends votre inquiétude. Si vous n'utilisez pas fetch, votre requête renverra uniquement les employés. Si les départements, même dans ce cas, continuent à être retournés, c'est parce que votre mappage entre Employee et Department (un @OneToMany) est défini avec FetchType.EAGER. Dans ce cas, toute fetchrequête HQL (avec ou non) avec FROM Employeeapportera tous les départements.
Dherik
Sans utiliser fetch (terme de jointure uniquement), le résultat serait un tableau de collections, deux lignes, la première est une collection d'employés et la seconde est une collection de départements. En utilisant la récupération rapide ou la récupération paresseuse, les départements seront récupérés.
Bilal BBB
Sans récupération sur HQL, cela ne se produira que si votre mappage entre Employee et Department est EAGER ( @OneToMany(fetch = FetchType.EAGER). Si ce n'est pas le cas, les Départements ne seront pas restitués.
Dherik
@Dherik essayez-le vous-même, vous obtiendrez une ClassCastException.
Bilal BBB
Je découvre le problème. Ce n'est pas un problème de récupération, mais comment le a selectété fait dans le HQL. Essayez SELECT emp FROM Employee emp JOIN FETCH emp.department dep. JPA / Hibernate ont ce comportement d'un retour Listde Object[]quand vous OMMISSIONS la SELECTpartie.
Dherik