web-dev-qa-db-fra.com

Les requêtes natives JPA / Hibernate ne reconnaissent pas les paramètres

J'utilise Hibernate/JPA pour exécuter des requêtes PostGIS natives. Le problème avec ces requêtes est qu'elles ont besoin de paramètres qui ne sont pas de la forme classique X = "valeur".

Par exemple, les lignes suivantes plantent

 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 requête suivante fonctionne cependant:

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

(mais il est sujet à l'injection SQL ...)

Quelqu'un sait-il comment utiliser setParameter() dans ce cas?

45
user99054

L'utilisation de paramètres nommés n'est pas définie pour les requêtes natives. D'après la spécification JPA (section .6.3 Paramètres nommés):

Les paramètres nommés suivent les règles des identifiants définies dans la section 4.4.1. L'utilisation de paramètres nommés s'applique au Java langage de requête de persistance, et n'est pas défini pour les requêtes natives. Seule la liaison de paramètres positionnels peut être utilisée de manière portable pour les requêtes natives.

Essayez donc ce qui suit à la place:

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

Notez que dans JPA> = 2.0 vous pouvez utiliser les paramètres nommés dans les requêtes natives.

81
Pascal Thivent

Vous pouvez peut-être remplacer

'POINT(:lon :lat)'

avec

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

De cette façon, les paramètres sont en dehors des chaînes constantes et doivent être reconnus par l'analyseur de requêtes.

17
Jörn Horstmann

J'ai eu un problème similaire et j'ai constaté que les paramètres peuvent être définis avec des points d'interrogation dans les requêtes natives. Essaye ça:

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

L'idée était donc d'utiliser l'astuce de concaténation suggérée par Jörn Horstmann pour forcer les postgres à reconnaître les paramètres. Le code suivant fonctionne:

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

Merci beaucoup pour vos réponses!

2
user99054

Vous pouvez également vous débarrasser de l'ensemble

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

appeler et le remplacer par

ST_Point(:lon,:lat)

Ensuite, vous n'avez pas à vous soucier des devis.

2
MDAHatter

J'ai rencontré un problème similaire. J'utilisais une requête native dans le référentiel avec? 1. Il l'a résolu en entourant le paramètre entre crochets comme suit.

SELECT * FROM XYZ WHERE ABC = (?1)

http://javageneralist.blogspot.com/2011/06/jpa-style-positional-param-was-not.html

2
yousafsajjad

La réponse de Pascal est correcte, mais ... Comment votre solution est-elle sujette à l'injection SQL? Si vous utilisez String.format et type de parmater %f dans votre exemple, alors autre chose que nombre lève Java.util.IllegalFormatConversionException. Il n'y a pas de valeur de passage possible comme "xxx 'OR 1 = 1 -".

Soyez prudent, en utilisant %s dans String.format est prêt pour l'injection SQL.

2
Boris Šuška