web-dev-qa-db-fra.com

Hibernate: Comment définir la valeur du paramètre de requête NULL avec HQL?

comment puis-je définir un paramètre Hibernate sur "null"? Exemple:

Query query = getSession().createQuery("from CountryDTO c where c.status = :status  and c.type =:type")
.setParameter("status", status, Hibernate.STRING)
.setParameter("type", type, Hibernate.STRING);

Dans mon cas, le statut String peut être nul. J'ai débogué ceci et hibernate génère ensuite une chaîne/requête SQL comme celle-ci ....status = null... Cela ne fonctionne cependant pas dans MYSQL, car la bonne instruction SQL doit être "status is null "(Mysql ne comprend pas status = null et évalue ceci à false afin qu'aucun enregistrement ne soit jamais renvoyé pour la requête, d'après les documents mysql que j'ai lus ...)

Mes questions:

  1. Pourquoi Hibernate ne traduit-il pas correctement une chaîne nulle en "is null" (et crée plutôt, à tort, "= null")?

  2. Quel est le meilleur moyen de réécrire cette requête afin qu'elle soit null-safe? Avec nullsafe, je veux dire que dans le cas où la chaîne "status" est null, il convient de créer un "is null"?

Merci beaucoup! Tim

56
tim
  1. Je crois qu'hibernate traduit d'abord votre requête HQL en SQL et ensuite seulement, il tente de lier vos paramètres. Ce qui signifie qu'il ne pourra pas réécrire la requête de param = ? à param is null.

  2. Essayez d'utiliser Criteria api:

    Criteria c = session.createCriteria(CountryDTO.class);
    c.add(Restrictions.eq("type", type));
    c.add(status == null ? Restrictions.isNull("status") : Restrictions.eq("status", status));
    List result = c.list();
    
39
Sergey Demin

Ce n'est pas un problème spécifique à Hibernate (c'est juste la nature de SQL), et OUI, il IS une solution pour SQL et HQL:

@ Peter Lang a eu la bonne idée, et vous avez eu la bonne requête HQL. Je suppose que vous avez juste besoin d'une nouvelle exécution pour prendre en compte les modifications de la requête ;-)

Le code ci-dessous fonctionne absolument et c'est génial si vous conservez toutes vos requêtes dans orm.xml

from CountryDTO c where ((:status is null and c.status is null) or c.status = :status) and c.type =:type

Si votre paramètre String est null, la requête vérifie également si le statut de la ligne est null. Sinon, il faudra comparer avec le signe égal.

Notes:

Il peut s'agir d'un problème particulier lié à MySql. Je n'ai testé qu'avec Oracle.

La requête ci-dessus suppose qu'il existe des lignes de table où c.status est null

La clause where est priorisée afin que le paramètre soit vérifié en premier.

Le nom de paramètre 'type' peut être un mot réservé dans SQL mais cela ne devrait pas être grave car il est remplacé avant que la requête ne soit exécutée.

Si vous deviez ignorer le: status where_clause complètement; vous pouvez coder comme ceci:

from CountryDTO c where (:status is null or c.status = :status) and c.type =:type

et cela équivaut à:

sql.append(" where ");
if(status != null){
  sql.append(" c.status = :status and ");
}
sql.append(" c.type =:type ");
26
egallardo

Le javadoc for setParameter(String, Object) est explicite, indiquant que la valeur de l'objet doit être non nulle. C'est dommage qu'il ne lève pas d'exception si une valeur null est passée, cependant.

Une alternative est setParameter(String, Object, Type) , qui autorise les valeurs NULL, bien que je ne sois pas sûr de ce que Type paramètre serait le plus approprié ici.

12
skaffman

Il semble que vous deviez utiliser is null dans HQL, (ce qui peut conduire à des permutations complexes s’il existe plusieurs paramètres avec un potentiel nul), mais voici une solution possible:

String statusTerm = status==null ? "is null" : "= :status";
String typeTerm = type==null ? "is null" : "= :type";

Query query = getSession().createQuery("from CountryDTO c where c.status " + statusTerm + "  and c.type " + typeTerm);

if(status!=null){
    query.setParameter("status", status, Hibernate.STRING)
}


if(type!=null){
    query.setParameter("type", type, Hibernate.STRING)
}
5
Eran Medan

Je n’ai pas essayé cela, mais que se passe-t-il lorsque vous utilisez :status deux fois pour vérifier NULL?

Query query = getSession().createQuery(
     "from CountryDTO c where ( c.status = :status OR ( c.status IS NULL AND :status IS NULL ) ) and c.type =:type"
)
.setParameter("status", status, Hibernate.STRING)
.setParameter("type", type, Hibernate.STRING);
4
Peter Lang

HQL prend en charge coalesce , permettant des solutions de contournement abominables telles que:

where coalesce(c.status, 'no-status') = coalesce(:status, 'no-status')
4
Arjan

Pour une requête HQL réelle:

FROM Users WHERE Name IS NULL
3
Chris S

Vous pouvez utiliser

Restrictions.eqOrIsNull("status", status)

au lieu de

status == null ? Restrictions.isNull("status") : Restrictions.eq("status", status)
1
onexdrk

Voici la solution que j'ai trouvée sur Hibernate 4.1.9. J'ai dû passer un paramètre à ma requête qui peut parfois avoir la valeur NULL. J'ai donc passé l'utilisation de:

setParameter("orderItemId", orderItemId, new LongType())

Après cela, j'utilise la clause where suivante dans ma requête:

where ((:orderItemId is null) OR (orderItem.id != :orderItemId))

Comme vous pouvez le constater, j’utilise la méthode Query.setParameter (String, Object, Type), pour laquelle je n’ai pas pu utiliser le fichier Hibernate.LONG que j’ai trouvé dans la documentation (probablement sur des versions plus anciennes). Pour un ensemble complet d'options de paramètre type, consultez la liste de la classe d'implémentation de l'interface org.hibernate.type.Type.

J'espère que cela t'aides!

1