web-dev-qa-db-fra.com

Hibernate Union alternatives

Quelles sont les alternatives pour implémenter une requête d'union avec hibernate? Je sais que hibernate ne prend pas en charge les requêtes des syndicats pour le moment. Pour le moment, la seule façon de créer un syndicat consiste à utiliser un tableau d'affichage.

L'autre option est d'utiliser plain jdbc, mais de cette façon, je perdrais tous mes goodies de requête exemple/critère, ainsi que la validation du mappage hibernate effectuée par hibernate par rapport aux tables/colonnes.

51
Miguel Ping

Utilisez VIEW. Les mêmes classes peuvent être mappées vers différentes tables/vues à l'aide du nom de l'entité, de sorte que vous n'aurez même pas beaucoup de duplication. Être là, ça fait, ça marche.

JDBC simple a un autre problème caché: il n'a pas connaissance du cache de session Hibernate. Par conséquent, si un élément est mis en cache jusqu'à la fin de la transaction et n'est pas vidé de la session Hibernate, la requête JDBC ne le trouvera pas. Pourrait être très déroutant parfois.

26
Vladimir Dyuzhev

Vous pouvez utiliser id in (select id from ...) or id in (select id from ...)

par exemple. au lieu de inactif

from Person p where p.name="Joe"
union
from Person p join p.children c where c.name="Joe"

vous pourriez faire

from Person p 
  where p.id in (select p1.id from Person p1 where p1.name="Joe") 
    or p.id in (select p2.id from Person p2 join p2.children c where c.name="Joe");

Au moins, si vous utilisez MySQL, vous rencontrerez des problèmes de performances plus tard. Il est parfois plus facile de joindre un pauvre homme à deux requêtes:

// use set for uniqueness
Set<Person> people = new HashSet<Person>((List<Person>) query1.list());
people.addAll((List<Person>) query2.list());
return new ArrayList<Person>(people);

Il est souvent préférable de faire deux requêtes simples qu'un complexe.

MODIFIER:

pour donner un exemple, voici la sortie EXPLAIN de la requête MySQL obtenue à partir de la solution de sous-sélection:

mysql> explain 
  select p.* from PERSON p 
    where p.id in (select p1.id from PERSON p1 where p1.name = "Joe") 
      or p.id in (select p2.id from PERSON p2 
        join CHILDREN c on p2.id = c.parent where c.name="Joe") \G
*************************** 1. row ***************************
           id: 1
  select_type: PRIMARY
        table: a
         type: ALL
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: 247554
        Extra: Using where
*************************** 2. row ***************************
           id: 3
  select_type: DEPENDENT SUBQUERY
        table: NULL
         type: NULL
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: NULL
        Extra: Impossible WHERE noticed after reading const tables
*************************** 3. row ***************************
           id: 2
  select_type: DEPENDENT SUBQUERY
        table: a1
         type: unique_subquery
possible_keys: PRIMARY,name,sortname
          key: PRIMARY
      key_len: 4
          ref: func
         rows: 1
        Extra: Using where
3 rows in set (0.00 sec)

Plus important encore, 1. row n'utilise pas d'index et prend en compte 200k + lignes. Mal! L'exécution de cette requête a pris 0,7 seconde alors que les deux sous-requêtes sont en millisecondes. 

62
sfussenegger

Je suis d'accord avec Vladimir. Moi aussi, je me suis penché sur l’utilisation de UNION dans HQL et je n’ai trouvé aucun moyen de le contourner. Ce qui est étrange, c’est que j’ai trouvé (dans la FAQ d’Hibernate) que UNION n’est pas prise en charge, que les rapports de bogues relatifs à UNION sont marqués «fixes», que des groupes de discussion indiquent que les déclarations seront tronquées sous UNION, bien .... Après une journée de travail, j’ai finalement reporté mon portage HQL vers SQL, mais le faire dans une vue dans la base de données serait une bonne option. Dans mon cas, des parties de la requête ont été générées dynamiquement. J'ai donc dû créer le code SQL dans le code.

6
CodingWithSpike

J'ai une solution pour un scénario critique (pour lequel j'ai beaucoup lutté) avec l'union dans HQL.

par exemple. Au lieu de ne pas travailler: -

select i , j from A a  , (select i , j from B union select i , j from C) d where a.i = d.i 

OR

select i , j from A a  JOIN (select i , j from B union select i , j from C) d on a.i = d.i 

VOUS pourriez faire dans Hibernate HQL ->

Query q1 =session.createQuery(select i , j from A a JOIN B b on a.i = b.i)
List l1 = q1.list();

Query q2 = session.createQuery(select i , j from A a JOIN C b on a.i = b.i)
List l2 = q2.list();

alors vous pouvez ajouter les deux liste ->

l1.addAll(l2);
4
Vijay Gupta

Une vue est une meilleure approche, mais comme hql renvoie généralement une liste ou un ensemble ... vous pouvez faire list_1.addAll (list_2). Totalement nul par rapport à un syndicat mais devrait fonctionner.

3
Patrick

J'ai peut-être un problème plus simple à résoudre. Mon "par exemple" était dans JPA avec Hibernate en tant que fournisseur JPA.

J'ai divisé les trois sélections (deux dans un deuxième cas) en sélections multiples et combiné les collections renvoyées moi-même, remplaçant ainsi un «syndicat tous». 

2
Walt

J'ai moi aussi traversé cette épreuve - si la requête est générée de manière dynamique (par exemple, Critères d'Hibernate), je ne pourrais pas trouver un moyen pratique de le faire. 

La bonne nouvelle pour moi est que je n’enquêtais que sur l’union pour résoudre un problème de performances lors de l’utilisation d’un "ou" dans une base de données Oracle. 

La solution proposée par Patrick (combiner les résultats par programmation à l’aide d’un ensemble) alors que moche (surtout que je voulais aussi faire une pagination des résultats) me convenait parfaitement.

0
Daniel Alexiuc

Voici un cas particulier, mais qui pourrait vous inciter à créer votre propre travail. L'objectif ici est de compter le nombre total d'enregistrements de deux tables différentes lorsque les enregistrements répondent à un critère particulier. Je crois que cette technique fonctionnera dans tous les cas où vous devez regrouper des données provenant de plusieurs tables/sources. 

J'ai quelques classes spéciales intermédiaires configurées, donc le code qui appelle la requête nommée est court et simple, mais vous pouvez utiliser la méthode que vous utilisez normalement avec des requêtes nommées pour exécuter votre requête. 

QueryParms parms=new QueryParms();
parms.put("PROCDATE",PROCDATE);

Long pixelAll = ((SourceCount)Fetch.row("PIXEL_ALL",parms,logger)).getCOUNT();

Comme vous pouvez le voir ici, la requête nommée commence à ressembler énormément à une déclaration d'union:

@Entity
@NamedQueries({
        @NamedQuery(
            name  ="PIXEL_ALL",
            query = "" +
                    "  SELECT new SourceCount(" +
                    "     (select count(a) from PIXEL_LOG_CURR1 a " +
                    "       where to_char(a.TIMESTAMP, 'YYYYMMDD') = :PROCDATE " +
                    "     )," +
                    "     (select count(b) from PIXEL_LOG_CURR2 b" +
                    "       where to_char(b.TIMESTAMP, 'YYYYMMDD') = :PROCDATE " +
                    "     )" +
                    ") from Dual1" +
                    ""
    )
})

public class SourceCount {
    @Id
    private Long   COUNT;

    public SourceCount(Long COUNT1, Long COUNT2) {
        this.COUNT = COUNT1+COUNT2;
    }

    public Long getCOUNT() {
        return COUNT;
    }

    public void setCOUNT(Long COUNT) {
        this.COUNT = COUNT;
    }
}

Une partie de la magie ici consiste à créer une table factice et à y insérer un enregistrement. Dans mon cas, je l'ai nommé dual1 parce que ma base de données est Oracle, mais je ne pense pas que le nom de la table factice importe peu.

@Entity
@Table(name="DUAL1")
public class Dual1 {
    @Id
    Long ID;
}

N'oubliez pas d'insérer votre fiche factice:

SQL> insert into dual1 values (1);
0
wryan



Comme Patrick l'a dit, ajouter laLISTEs de chaqueSELECTserait une bonne idée, mais rappelez-vous qu'il s'agit de UNION ALL . Pour éviter cet effet secondaire, il suffit de contrôler si l'objet est déjà ajouté à la collection finale ou non. Si non, alors ajoutez-le .
Une autre chose à laquelle vous devriez vous intéresser est que si vous avez unJOINdans chaqueS&EACUTE;LECTIONNEZ, le résultat sera une liste de tableaux d’objets (List<Objetc[]>), il vous faudra donc parcourez-le pour ne garder que l'objet dont vous avez besoin .

Esperons que ça marche.

0
Arash moradabadi