2010-06-29 6 views
40

Ich verwende Hibernate/JPA, um native PostGIS-Abfragen auszuführen. Das Problem bei diesen Abfragen besteht darin, dass sie Parameter benötigen, die nicht der klassischen X = "Wert" -Form entsprechen.JPA/Ruhezustands-Abfragen erkennen Parameter nicht

Zum Beispiel zum Absturz bringen die folgenden Zeilen

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) 

Die folgende Abfrage funktioniert jedoch:

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

(aber es ist SQL-Injektion anfällige ...)

niemand wissen, wie man in diesem Fall setParameter() verwendet?

Antwort

17

Vielleicht können Sie

'POINT(:lon :lat)' 

mit

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

Auf diese Weise sind die Parameter außerhalb des konstanten Strings ersetzen und sollte von der Abfrage-Parser erkannt werden.

+0

Waoo ... Vielen Dank! das scheint zu funktionieren !!! – user99054

+3

+1 Interessanter Trick, der das Problem tatsächlich bestätigt, kommt von den Anführungszeichen, nicht von benannten Parametern (die man mit JPA jedoch vermeiden sollte). –

+0

Funktioniert in Spring-Daten mit NamedNativeQuery-Annotation, erfordert also keine Erstellung der Repository-Implementierung. – lreeder

77

Die Verwendung von benannten Parametern ist für native Abfragen nicht definiert. Von der JPA-Spezifikation (Abschnitt 3.6.3 benannte Parameter):

Benannte Parameter folgen den Regeln für Identifikatoren in Abschnitt 4.4.1 definiert. Die Verwendung der benannten Parameter gilt für der Java Persistence-Abfragesprache und ist nicht für systemeigene Abfragen definiert. Nur positionale Parameterbindung kann portabel für native Abfragen verwendet werden.

So versuchen die folgenden statt:

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

Beachten Sie, dass in JPA> = 2,0 Sie benannte Parameter in nativen Abfragen verwenden können.

+4

+1 für die Angabe der Spezifikation und daran erinnern, dass dies nicht tragbar ist. Ich habe tatsächlich benannte Parameter in nativen Abfragen in einem Hibernate-Projekt verwendet, was gut funktioniert. In einem anderen älteren Projekt, basierend auf toplink Wesentliche, alle nativen Abfragen verwenden Positionsparameter, also muss ich diese vor und vergessen gekannt haben :) –

+0

Hallo, Dank für Ihre Antwort. Es scheint jedoch immer noch auf setParameter (1, Länge) abzustürzen org.hibernate.QueryParameterException: Position über die Anzahl der deklarierten ordinal Parameter hinaus. Denken Sie daran, dass Ordinalparameter 1-basiert sind! Position: 1 keine Idee? – user99054

+1

@samokk: Ich frage mich, ob die einzelnen Anführungszeichen um POINT hier nicht das Problem sind. –

2

Die Idee war also, den von Jörn Horstmann vorgeschlagenen Verkettungstrick zu verwenden, um postgres zu zwingen, die Parameter zu erkennen. Der folgende Code funktioniert:

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

Vielen Dank für Ihre Antworten!

+1

Eigentlich sind es nicht Postgres, die die Parameter nicht erkannt haben, sondern Ihr JPA-Provider (wegen der einfachen Anführungszeichen). –

2

Sie können auch von der ganzen

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

Anruf und ersetzen sie durch

ST_Point(:lon,:lat) 

loszuwerden Dann müssen Sie sich nicht um Zitate kümmern.

2

Pascals Antwort ist korrekt, aber ... Wie ist Ihre Lösung SQL-Injektion anfällig? Wenn Sie String.format und parmater Typ %f in Ihrem Beispiel verwenden dann irgendetwas anderes als Nummer wirft java.util.IllegalFormatConversionException. Es gibt keine Möglichkeit, einen Wert wie "xxx 'OR 1 = 1 -" zu übergeben.

Seien Sie vorsichtig, mit %s in String.format ist SQL Injection bereit.

4

Ich hatte ein ähnliches Problem und festgestellt, dass Parameter mit Fragezeichen in nativen Abfragen gesetzt werden können. Versuchen Sie dies:

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