2010-01-23 51 views
52

¿cómo puedo establecer un parámetro de Hibernación en "nulo"? Ejemplo:Hibernate: ¿Cómo establecer el valor del parámetro de consulta NULL con HQL?

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); 

En mi caso, la cadena de estado puede ser nula. He depurado esto e hibernate luego genera una cadena/consulta SQL como esta ... status = null ... Sin embargo, esto no funciona en MYSQL, ya que la declaración SQL correcta debe ser "status is null" (Mysql no entiende status = null y evalúa a false de manera que no hay registros jamás serán devueltos por la consulta, de acuerdo con la documentación de MySQL que he leído ...)

Mis preguntas:

  1. por qué doesnt Hibernate traducir una cadena nula correctamente a "es nulo" (y más bien y crea erróneamente "= nulo")?

  2. ¿Cuál es la mejor manera de volver a escribir esta consulta para que sea nula? Con nullsafe quiero decir que en el caso de que el "estado" String sea nulo de lo que debería crear un "es nulo"?

Muchas gracias! Tim

+0

Para aquellos interesados ​​en una solución, creo que la API de Criteria es una manera de hacerlo. Pero todavía no estoy muy convencido ya que carga el código horriblemente y usar HQL sería mucho más limpio.Tal vez la solución real es implementar su propio tipo Hibernate (he implementado uno para ENUM pero estos tipos auto implementados, al menos los básicos tienen grandes desventajas sobre el bulid en tipos de hibernación en Querying with HQL (a menos que también extienda el HQL) analizador?) lo que hace que este sea un gran proyecto y requiera mucho conocimiento de Hibernate ... (continúa en la parte 2) – tim

+0

Parte2: Quizás el más fácil (por otro lado, NO es una Buena Práctica) está editando directamente la clase Hibernate String Type sí mismo y agregue la lógica que falta. Esto debería ser solo una línea de vista para verificar valores de Cadena nulos y actuar en consecuencia ... – tim

+3

Acabo de tropezar con esto también ... Dios mío, esto es muerte de cerebros. –

Respuesta

37
  1. Creo hibernación se traduce primero la consulta HQL a SQL y sólo después de que intenta enlazar sus parámetros. Lo que significa que no podrá reescribir la consulta de param = ? a param is null.

  2. Try usando los criterios del 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(); 
    
+1

Hola Sergey, gracias por su respuesta. En realidad lo implementé (ya antes) publicaste tu respuesta) también con la API Criteria. Sin embargo, creo que esta no es la "solución ideal" para el problema. Ok, para ser sincero, a veces las "rarezas/errores" no tienen una solución realmente buena ... ha marcado su respuesta como la "solución" al problema. – tim

+0

es bienvenido :) pero ¿qué espera de la solución ideal? Si está satisfecho con la API api en lugar de hql, siempre puede crear su propio método de fábrica para el Criterio 'nulo'. –

+0

¡Gran respuesta! ¡Esta solución simple me ayudó a consultar contra un campo que admite valores NULL sin recurrir a la búsqueda de cadenas de construcción! – cdeszaq

11

El javadoc for setParameter(String, Object) es explícito, indicando que el valor del Objeto debe ser no nulo. Es una pena que no arroje una excepción si se pasa un nulo.

Una alternativa es setParameter(String, Object, Type), que qué permiten valores nulos, aunque no estoy seguro de lo que Type parámetro sería más apropiado en este caso.

+0

Hola skaffman. ¡Muchas gracias por su ayuda! Al hacer la pregunta, en realidad pensé que era una tontería encontrar una solución. Pero parece que esta es una verdadera "rareza" de hibernación. Lo más probable es que tenga que escribir mi propio Nullable-String-Type o tendré que cambiar a las consultas de criterios ... Después de profundizar parece que la implementación actual del tipo Hibernat.STRING no puede contener nulos: http: // www.docjar.com/html/api/org/hibernate/type/StringType.java.html. (¿Pero no debería el MySQL JConnector implementar el PreparedStatment.setString correctamente?) Gracias Tim. – tim

+2

Solo para obtener información, en realidad es una "característica" de JDBC: http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4312435 Hibernate usa 'PreparedStatement.setNull()' de JDBC para unir parámetros 'null' – axtavt

4

No probé esto, pero ¿qué sucede cuando usa :status dos veces para comprobar 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); 
+0

Hellp Peter, muchas gracias por tu ayuda! No sé por qué, pero parece que hibernate siempre evalúa el (c.status =: estado OR (c.status IS NULL AND: el estado IS NULL) a c.status = null (de acuerdo con mi registro de consultas mysql). También cambié el orden a ((c.status IS NULL Y: status IS NULL) O c.status =: status) con el mismo resultado. Gracias Tim – tim

4

Parece que tienes que usar is null en el HQL, (que puede conducir a permutaciones complejos si hay más de uno parámetros con potencial nulo.) pero aquí es una solución posible:

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) 
} 
+3

Parece que la creación de consultas SQL antiguas me parece. Mantente en la API de criterios para cosas como esta. Escrito a mano hql es extremadamente propenso a errores. – whiskeysierra

3

Para una consulta real HQL:

FROM Users WHERE Name IS NULL 
1

Puede utilizar

Restrictions.eqOrIsNull("status", status) 

insted de

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

Aquí está la solución que encontré en Hibernate 4.1.9.Tuve que pasar un parámetro a mi consulta que puede tener valor NULL a veces. Así pasé el uso de:

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

Después de eso, utilizo la siguiente cláusula where en mi consulta:

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

Como se puede ver, estoy usando el Query.setParameter (String, Object, Tipo) método, donde no pude usar el Hibernate.LONG que encontré en la documentación (probablemente eso estaba en versiones anteriores). Para obtener un conjunto completo de opciones de tipo de parámetro, consulte la lista de la clase de implementación de la interfaz org.hibernate.type.Type.

Espero que esto ayude!

22

Esto no es un problema específico de hibernación (es sólo la naturaleza de SQL), y sí, hay una solución para SQL y HQL:

@Peter Lang tenía la idea correcta, y que tenía la consulta HQL correcta . Creo que se acaba de necesitar una nueva carrera limpia para recoger cambia la consulta ;-)

El código siguiente funciona en absoluto y es genial si mantiene todas sus consultas en orm.xml

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

Si su parámetro String es nulo, la consulta comprobará si el estado de la fila también es nulo. De lo contrario, recurrirá a comparar con el signo igual.

Notas:

El tema puede ser una peculiaridad específica MySql. Solo probé con Oracle.

La consulta anterior asume que hay filas de la tabla donde c.status es nulo

La cláusula where se prioriza para que el parámetro se comprueba primero.

El nombre del parámetro 'tipo' puede ser una palabra reservada en SQL pero no debería importar, ya que se reemplaza antes de que se ejecute la consulta.

Si necesita omitir el: estado where_clause en total; Puede codificar así:

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

y es equivalente a:

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

HQL soporta coalesce, lo que permite soluciones feas como:

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

esto parece funcionar como wel ->

@Override 
public List<SomeObject> findAllForThisSpecificThing(String thing) { 
    final Query query = entityManager.createQuery(
      "from " + getDomain().getSimpleName() + " t where t.thing = " + ((thing == null) ? " null" : " :thing")); 
    if (thing != null) { 
     query.setParameter("thing", thing); 
    } 
    return query.getResultList(); 
} 

Btw, soy bastante nuevo en esto, así que si por alguna razón esta no es una buena idea, házmelo saber. Gracias.

+0

¿No debería estar usando el operador 'is': es decir,' ... + "t donde t.thing" + ((thing == null)? "Es nulo": "=: thing") ' – bvdb

+0

Podría elaborar sobre la diferencia entre esos dos? –

+2

El uso de '= null' causará una excepción. Aunque puede depender de qué dbms ejecuta esto. En SQL tiene el mismo problema, también allí tiene que usar 'es nulo' y no' = nulo'. Lo que pasa con 'null' es que algunas bases de datos no lo consideran como un valor para el vacío, sino más bien como un valor indeterminado, lo que significa que null no es nulo. Por lo tanto, 'null' =' null' nunca sería verdadero. Ver: http://stackoverflow.com/questions/5066492/hql-is-null-and-null-on-an-oracle-column – bvdb

Cuestiones relacionadas