Salut quelqu'un peut-il m'indiquer comment nous pouvons passer une clause order by en tant que paramètre nommé à hql
Ex:
Travaux:
select tb from TransportBooking as tb
and TIMESTAMP(tb.bookingDate, tb.bookingTime) >= current_timestamp() order by tb.bookingDate
Ne marche pas:
select tb from TransportBooking as tb
and TIMESTAMP(tb.bookingDate, tb.bookingTime) >= current_timestamp() order by :order
Non pris en charge, les paramètres d'entrée ne sont autorisés que dans les clauses WHERE
et HAVING
et vous ne pouvez pas utiliser de paramètres pour la clause ORDER BY
. Ou si je reformule, vous ne pouvez pas utiliser de paramètres pour les colonnes, mais uniquement les valeurs. Alors, soit:
Essayez de stocker la requête nommée sans la clause order by, en récupérant la chaîne de requête et en ajoutant les éléments de la clause order by au moment de l'exécution.
Brian Fields l'expliquait dans son blog: http://brainfields.blogspot.com/2009/08/order-by-in-in-hibernate-named-queries.html
J'ai emballé l'idée pour mon projet:
private static final Pattern badQueryPattern = Pattern.compile("[^\\p{ASCII}]*");
public static String getNamedQueryString(EntityManager em, String queryName) throws SQLException {
Query tmpQuery = em.createNamedQuery(queryName);
SQLQuery sqlQuery = tmpQuery.unwrap(SQLQuery.class);
String queryString = sqlQuery.getQueryString();
if (badQueryPattern.matcher(queryString).matches()) {
throw new SQLException("Bad query string.");
}
return queryString;
}
public static Query getNamedQueryOrderedBy(EntityManager em, String queryName, Map<String, Boolean> columnNames) throws SQLException {
StringBuilder sb = new StringBuilder();
sb.append(ORDER_BY_CLAUSE_START);
int limit = columnNames.size();
int i = 0;
for (String columnName: columnNames.keySet()) {
sb.append(columnName);
if (columnNames.get(columnName))
sb.append(" ASC");
else
sb.append(" DESC");
if (i != (limit - 1)) {
sb.append(", \n");
}
}
Query jpaQuery = em.createNativeQuery( getNamedQueryString(em, queryName)
+ sb.toString()
);
return jpaQuery;
}
Vous voudrez peut-être limiter le champ de tri à ceux que vous avez dans votre modèle. Dans mon projet, je l'ai fait statiquement:
public static boolean isColumnName(Object domain, String columnName) {
Field[] fields = domain.getClass().getDeclaredFields();
for (Field field : fields) {
Annotation[] annotations = field.getAnnotations();
for (Annotation annotation : annotations) {
if (annotation instanceof Column) {
Column column = (Column) annotation;
String foundColumnName;
if (column.name() != null && !column.name().isEmpty()) {
foundColumnName = column.name();
} else {
foundColumnName = field.getName();
}
if (columnName.toUpperCase().equals(
foundColumnName.toUpperCase())) {
return true;
}
}
}
}
return false;
}
En validant le nom du champ sur votre DAL avant de concatter la chaîne dans le jpql ou le hql, vous éviterez l’injection de SQL ou d’autres problèmes
Cela peut être fait comme ça
order by CASE :orderBy
WHEN 'pato_id' THEN PATO.id
WHEN 'last_update_dt' THEN PATO.last_update_dt
END desc
et vous pouvez passer "pato_id" ou "last_update_dt" dans la fonction setString comme ceci
q.setString("orderBy", "last_update_dt");
or
q.setString("orderBy", "pato_id");
Cela fonctionne avec MS SQL Server, pas sûr des autres.
Cela fonctionne pour moi dans MySQL:
ORDER BY CASE :orderby WHEN 0 THEN field_x WHEN 1 THEN field_y ELSE field_z END ASC