web-dev-qa-db-fra.com

HQL, jointure gauche d'entités non liées

J'ai 2 entités, A et B. Ils sont liés mais je ne veux pas ajouter le mappage de relation aux beans.

Comment utiliser la jointure externe gauche entre A et B en utilisant HQL ou des critères ?

Il existe des solutions de contournement pour cela,

  1. Utilisez Native SQL comme indiqué ici .
  2. Ajoutez une relation et utilisez sélectionnez a parmi A une jointure gauche a.b .
  3. On peut faire une jointure interne dans le HQL comme sélectionner * dans A a, B b où a.some = b.some

J'étais toujours en train de revenir sur ces 2 options, y a-t-il une alternative pour cela? Ou ce n'est pas possible?

44
ManuPK

Actuellement, le style thêta pour joindre les classes non liées dans la clause where en utilisant HQL ne prend en charge que la jointure interne.

La demande pour supporter la jointure externe dans une telle situation est actuellement la 3ème amélioration la plus votée mais je ne pense pas que cette fonctionnalité sera implémentée dans la fonctionnalité proche car elle nécessite la réimplémentation de l'analyseur de requêtes actuel basé sur ANTLER d'abord, ce qui semble être une tâche IMO gigantesque.

Si vous insistez pour utiliser le HQL pour effectuer la jointure gauche sans ajouter la relation entre A et B, vous pouvez utiliser l'option 3 pour effectuer la jointure interne en premier, puis utilisez le HQL suivant

from A a where a.some not in ( select b.some from B)

pour découvrir tous les A qui ne peuvent pas rejoindre B et combiner les résultats par programme.

Mise à jour

Depuis la version 5.1.0 HHH-16 (jointures explicites sur les classes non liées) est corrigé et nous devrions pouvoir rejoindre les entités non liées.

57
Ken Chan

Comme l'a dit Ken Chan, vous ne pouvez pas le faire directement dans une seule requête HQL.

Concernant vos trois possibilités:

  1. SQL natif: déconseillé. La syntaxe des jointures externes est très différente entre les différentes bases de données.
  2. Ajouter une relation: c'est ce que je ferais. Il ne coûte pas beaucoup de code ou de mémoire et il est rapidement programmé.
  3. Jointure interne: cela ne fonctionne pas (lignes manquantes) si la relation est réellement une jointure externe dans la base de données.

Si, pour des raisons particulières, vous ne voulez vraiment pas ajouter la relation, vous pouvez diviser la requête en deux requêtes individuelles et joindre le résultat manuellement en Java, par exemple comme ceci:

Query qa = session.createQuery("from A a");
List la = qa.list();

Query qb = session.createQuery("select distinct b.* from B b, A a where a.some=b.some");
List lb = qb.list();

Map bMap = new HashMap();
for (B b : lb) {
  bMap.put(b.getId(), b);
}

/* example with for loop */
for (A a : la) {
  B b = bMap.get(a.getForeignKeyForB());
  /* now you have A a and the outer joined B b and you can do with them what you want */
  ...
}

Cette solution a (presque) le même coût en temps d'exécution et en mémoire que la jointure externe dans la base de données (solution 2.). C'est juste un peu plus Java code.

(La solution est similaire à celle proposée par Ken Chan, mais elle évite le "pas dedans" et la sélection interne, qui peuvent toutes deux être inefficaces dans la base de données.)

3
Johanna

Si vous savez que pour chaque A, il y a au maximum 1 B, vous pouvez également utiliser une sous-requête.

Par exemple:

select a, (select b from B b where b.some = a.some)
from A a

Si vous savez qu'il existe au moins 1 B, vous pouvez également utiliser la requête suivante, mais elle n'est pas recommandée car il s'agit d'un hack:

select a, (select b4 from B b4 where b4=b and b4.some=a.some) from A a, B b 
where a.some=b.some or (a.some not in (select b2.some from B b2) 
and b.id = (select min(b3.id) from B b3))
1
Wim De Rammelaere