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();
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.
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 /
Exécutez deux sélections, une filtrée pour toutes les chaînes commençant par "XX", la seconde filtrée pour les autres.
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;
}
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.
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:
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.