web-dev-qa-db-fra.com

Comment commander le résultat de la mise en veille prolongée en fonction d'une commande spécifique

J'ai besoin d'envoyer une requête pour récupérer des valeurs qui ont un groupe spécifique de caractères comme suit:

Disons que je suis intéressé par 'XX' donc il devrait rechercher n'importe quel champ dont la valeur commence par 'XX' ou a 'XX' (espace XX). Par exemple XXCDEF, PD XXRF et CMKJIEK XX sont des résultats valides.

J'ai la requête suivante qui renvoie les résultats corrects mais je dois les trier de manière à ce qu'ils retournent d'abord ceux avec XX au début puis d'autres résultats. Comme suit:

XXABCD
XXPLER
XXRFKF
AB XXAB
CD XXCD
ZZ XXOI
POLO XX

Code

Criteria criteria = session.createCriteria(Name.class, "name")
                .add(Restrictions.disjunction()
                     .add(Restrictions.ilike("name.fname", fname + "%"))
                     .add(Restrictions.ilike("name.fname", "%" + " " + fname + "%"))
                    )
                .setProjection(Projections.property("name.fname").as("fname"));
        List<String> names = (List<String>) criteria.list();
23
Jack

Avec JPQL (HQL):

select fname from Name
where upper(fname) like :fnameStart or upper(fname) like :fnameMiddle
order by (case when upper(fname) like :fnameStart then 1 else 2 end), fname

query.setParameter("fnameStart", "XX%");
query.setParameter("fnameMiddle", "% XX%");

avec critères

Avec Criteria c'est beaucoup plus délicat. Tout d'abord, vous devez recourir au SQL natif dans la clause order. Deuxièmement, vous devez lier la variable.

public class FirstNameOrder extends Order {
    public FirstNameOrder() {
        super("", true);
    }

    @Override
    public String toSqlString(Criteria criteria, CriteriaQuery criteriaQuery) throws HibernateException {
        return "case when upper(FIRST_NAME) like ? then 1 else 2 end";
    }
}

La syntaxe de l'expression de casse et le nom de la fonction upper doivent être modifiés en fonction de votre base de données (et du nom de la colonne s'il est différent, bien sûr).

Il est facile d'ajouter ceci au Criteria, mais il n'y a pas d'API pour lier le paramètre.

J'ai essayé de tromper Hibernate en passant une variable inutilisée à la restriction sql personnalisée afin qu'elle soit effectivement utilisée pour la variable dans le order by clause:

Criteria criteria = session.createCriteria(Name.class, "name")
   .add(Restrictions.disjunction()
      .add(Restrictions.ilike("name.fname", fname + "%"))
      .add(Restrictions.ilike("name.fname", "%" + " " + fname + "%")))
   .setProjection(Projections.property("name.fname").as("fname"))
   .add(Restrictions.sqlRestriction("1 = 1", fname + "%", StringType.INSTANCE))
   .addOrder(new FirstNameOrder())
   .addOrder(Order.asc("fname"));

et ça marche bien.

Évidemment, cette solution n'est pas recommandée et je suggère d'utiliser JPQL pour cette requête.

22
Dragan Bozanovic

Hibernate prend en charge la commande: http://docs.jboss.org/hibernate/orm/4.2/devguide/en-US/html/ch11.html#ql-ordering En raison des critères spéciaux, je pense vous devez personnaliser la commande dans Hibernate. Ce lien peut aider: http://blog.tremend.ro/2008/06/10/how-to-order-by-a-custom-sql-formulaexpression-when-using-hibernate-criteria-api /

3
Kenny Tai Huynh

Exécutez deux sélections, une filtrée pour toutes les chaînes commençant par "XX", la seconde filtrée pour les autres.

2
wero

Vous pouvez utiliser des prédicats dans les critères ... quelque chose comme ceci:

public List<Name> findByParameter(String key, String value, String orderKey)

            CriteriaBuilder builder = this.entityManager.getCriteriaBuilder();
            CriteriaQuery<Name> criteria = builder.createQuery(this.getClazz());
            Root<Name> root = criteria.from(Name.getClass());
            criteria.select(root);
            List<Predicate> predicates = new ArrayList<Predicate>();
            predicates.add(builder.equal(root.get(key), value));
            criteria.where(predicates.toArray(new Predicate[predicates.size()]));
            if (orderKey!=null  && !orderKey.isEmpty()) {
                criteria.orderBy(builder.asc(root.get(orderKey)));
            }
            result = this.entityManager.createQuery(criteria).getResultList();

        return result;
}
1
Gabi Carballo

Si vous ne voulez pas trier le résultat en mémoire, vous pouvez modifier vos critères, je vais vous montrer le SQL

select * from table where fname like 'XX%' order by fname
union all
select * from table where fname like '% XX%' order by fname
union all
select * from table where fname like '% XX' order by fname

le résultat sera votre commande et alphabétique puis appliquez votre filtre.

0
George

Stupide mais cela peut fonctionner pour votre cas.

Puisque vous avez obtenu votre bon résultat, vous pouvez simplement reconstruire vos résultats comme suit:

  1. ramasser tous les résultats commençant par XX vous les mettez dans une liste L1 et faites le tri normal comme Collections.sort (L1);
  2. pour tous les autres résultats, faites la même chose que Collections.sort (L2) comme liste de L2
  3. Enfin, mettez-les ensemble

    List newList = new ArrayList (L1);

    newList.addAll (L2);

Notez s'il vous plaît. Collections.sort suit l'ordre naturel de ses éléments.

0
Jegg