2010-06-29 17 views
40

Estoy usando Hibernate/JPA para ejecutar consultas nativas de PostGIS. El problema con estas consultas es que necesitan parámetros que no son de la forma clásica X = 'valor'.Las consultas nativas JPA/Hibernate no reconocen los parámetros

Por ejemplo, las siguientes líneas chocan

String queryString = "select * from Cell c where ST_DWithin(c.shape, SetSRID(ST_GeomFromEWKT('POINT(:lon :lat)'),4326), 0.1)"; 
    Query query = Cell.em().createNativeQuery(queryString, Cell.class); 
    query.setParameter("lon", longitude); 
    query.setParameter("lat", latitude); 

play.exceptions.JavaExecutionException: org.hibernate.QueryParameterException: could not locate named parameter [lon] 
at play.mvc.ActionInvoker.invoke(ActionInvoker.java:259) 
at Invocation.HTTP Request(Play!) 
Caused by: java.lang.IllegalArgumentException: org.hibernate.QueryParameterException: could not locate named parameter [lon] 
at org.hibernate.ejb.QueryImpl.setParameter(QueryImpl.java:358) 

La siguiente consulta funciona sin embargo:

String queryString = String.format("select * from Cell c where ST_DWithin(c.shape, SetSRID(ST_GeomFromEWKT('POINT(%f %f)'),4326), 0.1)", longitude, latitude); 
Query query = Cell.em().createNativeQuery(queryString, Cell.class); 

(pero es la inyección de SQL-propensa ...)

¿Hay alguien ¿Sabes cómo usar setParameter() en este caso?

Respuesta

17

Tal vez se puede sustituir

'POINT(:lon :lat)' 

con

'POINT(' || :lon || ' ' || :lat || ')' 

De esta manera los parámetros están fuera de cadenas constantes y deben ser reconocidos por el analizador de consultas.

+0

waoo ... Gracias! ¡esto parece funcionar! – user99054

+3

+1 Truco interesante que realmente confirma que el problema proviene de las comillas, no de los parámetros nombrados (que uno debería evitar con JPA). –

+0

Funciona en Spring Data con la anotación NamedNativeQuery, por lo que no requiere la creación de la implementación de Repository. – lreeder

77

El uso de parámetros con nombre no está definido para consultas nativas. De la especificación JPA (sección 3.6.3 Named Parámetros):

parámetros con nombre sigue las reglas para identificadores definidos en la Sección 4.4.1. El uso de parámetros con nombre se aplica a el lenguaje de consulta de Java Persistence, y no está definido para consultas nativas. Solo el enlace de parámetro posicional puede ser utilizado de forma portátil para consultas nativas.

así que trata el siguiente lugar:

String queryString = "select * from Cell c where ST_DWithin(c.shape, SetSRID(ST_GeomFromEWKT('POINT(?1 ?2)'),4326), 0.1)"; 
Query query = Cell.em().createNativeQuery(queryString, Cell.class); 
query.setParameter(1, longitude); 
query.setParameter(2, latitude); 

Nótese que en la APP> = 2.0 se puede utilizar parámetros con nombre en consultas nativas.

+4

+1 por citar la especificación y recordarme que esto no es portátil. De hecho, he usado parámetros con nombre en consultas nativas en un proyecto de hibernación, que funciona bien. En otro proyecto anterior, basado en los elementos esenciales del enlace superior, todas las consultas nativas usan parámetros posicionales, por lo que debo haberlo sabido antes y olvidado :) –

+0

Hola, gracias por su respuesta. Sin embargo, todavía parece bloquearse en setParameter (1, longitud) org.hibernate.QueryParameterException: posición más allá del número de parámetros ordinales declarados. ¡Recuerde que los parámetros ordinales están basados ​​en 1! Posición: 1 ¿Alguna idea? – user99054

+1

@samokk: Me pregunto si las comillas simples alrededor de POINT no son el problema aquí. –

2

Entonces, la idea era usar el truco de concatenación sugerido por Jörn Horstmann para forzar a postgres a reconocer los parámetros. El siguiente código funciona:

String queryString = "select * from Cell c where ST_DWithin(c.shape, SetSRID(ST_GeomFromEWKT('POINT(' || :lon || ' ' || :lat || ')'),4326), 0.2)"; 
Query query = Cell.em().createNativeQuery(queryString, Cell.class); 
query.setParameter("lon", longitude); 
query.setParameter("lat", latitude); 

Muchas gracias por sus respuestas!

+1

En realidad, no es postgres que no reconoce los parámetros, sino su proveedor de JPA (debido a las comillas simples). –

2

También puede deshacerse de toda la

ST_GeomFromEWKT('POINT(' || :lon || ' ' || :lat || ')') 

llamada y reemplazarlo con

ST_Point(:lon,:lat) 

Entonces usted no tiene que preocuparse de las cotizaciones.

2

La respuesta de Pascal es correcta, pero ... ¿Cómo es su solución de inyección SQL propensa? Si está usando String.format y parmater escriba %f en su ejemplo, cualquier otra cosa que no sea el número arroja java.util.IllegalFormatConversionException. No hay ningún valor de pase posible como "xxx" O 1 = 1 - ".

Tenga cuidado, usando %s en String.format está listo para inyección SQL.

4

Tuve un problema similar y descubrí que los parámetros se pueden establecer con signos de interrogación en las consultas nativas. Pruebe esto:

String queryString = "select * from Cell c where ST_DWithin(c.shape, SetSRID(ST_GeomFromEWKT('POINT(? ?)'),4326), 0.1)"; 

Query query = Cell.em().createNativeQuery(queryString, Cell.class); 
query.setParameter(1, longitude); 
query.setParameter(2, latitude); 
Cuestiones relacionadas