Meilleures pratiques de codage Java pour la réutilisation d'une partie d'une requête à compter

StackOverflow https://stackoverflow.com/questions/1600440

Question

Le implementation-result-paging-in hibernate-getting-total-total-nombre-de-lignes pose une autre question sur un problème de mise en œuvre :

Vous savez maintenant que vous devez réutiliser une partie de la requête HQL pour effectuer le décompte, comment réutiliser efficacement?

Les différences entre les deux requêtes HQL sont les suivantes:

  1. la sélection est count (?) , au lieu du pojo ou de la propriété (ou de la liste de)
  2. les extractions ne doivent pas se produire, certaines tables ne doivent donc pas être jointes
  3. l'ordre par devrait disparaître

Y a-t-il d'autres différences?

Disposez-vous de meilleures pratiques de codage pour réussir cette réutilisation (préoccupations: effort, clarté, performance)?

Exemple pour une requête HQL simple:

    select       a     from A a join fetch a.b b where a.id=66 order by a.name
    select count(a.id) from A a                  where a.id=66

MISE À JOUR

J'ai reçu des réponses sur:

  • utilisant Critères (mais nous utilisons principalement HQL)
  • manipulant la requête String (mais tout le monde convient que cela semble compliqué et pas très sûr)
  • encapsulez la requête , en vous appuyant sur l'optimisation de la base de données (mais vous avez le sentiment que cela n'est pas sans danger)

J'espérais que quelqu'un donnerait des options le long d'un autre chemin, davantage lié à la concaténation de chaînes.
Pourrions-nous construire les deux requêtes HQL en utilisant des parties communes ?

Était-ce utile?

La solution

Belle question. Voici ce que j'ai déjà fait par le passé (beaucoup de choses que vous avez déjà mentionnées):

  1. Vérifiez si la clause SELECT est présente.
    1. Si ce n'est pas le cas, ajoutez select count (*)
    2. Sinon, vérifiez si elle contient DISTINCT ou des fonctions d'agrégation. Si vous utilisez ANTLR pour analyser votre requête, il est possible de les contourner, mais c'est très compliqué. Il est probablement préférable d’envelopper le tout avec select count (*) from () .
  2. Supprimer récupérer toutes les propriétés
  3. Supprimez fetch des jointures si vous analysez HQL sous forme de chaîne. Si vous analysez réellement la requête avec ANTLR, vous pouvez supprimer entièrement left join ; c'est assez compliqué de vérifier toutes les références possibles.
  4. Supprimer ordre par
  5. Selon ce que vous avez fait dans la version 1.2, vous devrez supprimer / ajuster le groupe en / ayant .

Ce qui précède s’applique naturellement à HQL. Pour les requêtes Critères, vous êtes assez limité avec ce que vous pouvez faire car cela ne se prête pas facilement à la manipulation. Si vous utilisez une sorte de couche wrapper au-dessus de Critères, vous obtiendrez un équivalent de sous-ensemble (limité) de résultats d'analyse ANTLR et pourrez appliquer la plupart des éléments ci-dessus dans ce cas.

Etant donné que vous vous en tenez normalement au décalage de votre page actuelle et du nombre total, j’exécute généralement la requête réelle avec la limite / le décalage donné, puis uniquement la requête count (*) si le nombre de résultats renvoyés est supérieur ou égal à la limite ET le décalage est égal à zéro (dans tous les autres cas, j'ai déjà exécuté le compte (*) ou tous les résultats sont restitués). C’est bien sûr une approche optimiste en ce qui concerne les modifications simultanées.

Mettre à jour (sur HQL assemblé à la main)

Je n'aime pas particulièrement cette approche. Lorsqu’il est mappé en tant que requête nommée, HQL présente l’avantage de la vérification des erreurs au moment de la construction (techniquement, au moment de l’exécution, car SessionFactory doit être construit bien que cela soit généralement fait lors des tests d’intégration). Lorsqu’elle est générée au moment de l’exécution, elle échoue à l’exécution :-) L’optimisation des performances n’est pas non plus facile.

Le même raisonnement s’applique à Criteria, bien sûr, mais c’est un peu plus difficile à gâcher à cause d’une API bien définie, par opposition à la concaténation de chaînes. Construire deux requêtes HQL en parallèle (une paginée et un "décompte global") conduit également à la duplication de code (et éventuellement à plus de bugs) ou vous oblige à écrire une sorte de couche d'enveloppe au dessus pour le faire pour vous. Les deux voies sont loin d'être idéales. Et si vous devez le faire à partir du code client (comme dans l'API), le problème s'aggrave encore.

J'ai en effet beaucoup réfléchi sur cette question. L'API de recherche de Hibernate-Generic-DAO semble être un compromis raisonnable; il y a plus de détails dans ma réponse à la question liée ci-dessus.

Autres conseils

Avez-vous essayé de préciser vos intentions à Hibernate en définissant une projection sur vos critères (SQL?)? J'ai principalement utilisé des critères, je ne sais donc pas si cela s'applique à votre cas, mais je l'utilise

getSession().createCriteria(persistentClass).
setProjection(Projections.rowCount()).uniqueResult()

et de laisser Hibernate découvrir le contenu caché / réutilisé / intelligent en soi .. Vous ne savez pas vraiment combien de choses intelligentes il fait réellement .. Quelqu'un veut-il commenter?

Eh bien, je ne suis pas sûr que ce soit une pratique exemplaire, mais c’est ma pratique:)

Si j'ai comme requête quelque chose comme:

select A.f1,A.f2,A.f3 from A, B where A.f2=B.f2 order by A.f1, B.f3

Et je veux juste savoir combien de résultats seront obtenus, j'exécute:

select count(*) from ( select A.f1, ... order by A.f1, B.f3 )

Et obtenez ensuite le résultat sous forme d'entier, sans mappage des résultats dans un POJO.

Analyser votre requête pour supprimer certaines pièces, comme "commander par" est très compliqué. Un bon SGBDR optimisera votre requête pour vous.

Bonne question.

Dans une situation HQL à main levée, j'utiliserais quelque chose comme ceci, mais ce n'est pas réutilisable car c'est assez spécifique pour les entités données

Integer count = (Integer) session.createQuery("select count(*) from ....").uniqueResult();

Faites ceci une fois et ajustez le numéro de départ en conséquence jusqu’à ce que vous parcouriez.

Pour les critères bien que j'utilise un échantillon comme celui-ci

final Criteria criteria = session.createCriteria(clazz);  
            List<Criterion> restrictions = factory.assemble(command.getFilter());
            for (Criterion restriction : restrictions)
                criteria.add(restriction);
            criteria.add(Restrictions.conjunction());
            if(this.projections != null)
                criteria.setProjection(factory.loadProjections(this.projections));
            criteria.addOrder(command.getDir().equals("ASC")?Order.asc(command.getSort()):Order.desc(command.getSort()));
            ScrollableResults scrollable = criteria.scroll(ScrollMode.SCROLL_INSENSITIVE);
            if(scrollable.last()){//returns true if there is a resultset
                genericDTO.setTotalCount(scrollable.getRowNumber() + 1);
                criteria.setFirstResult(command.getStart())
                        .setMaxResults(command.getLimit());
                genericDTO.setLineItems(Collections.unmodifiableList(criteria.list()));
            }
            scrollable.close();
            return genericDTO;

Mais cela compte chaque fois en appelant ScrollableResults: last () .

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top