49

我正在使用 Hibernate/JPA 来执行本机 PostGIS 查询。这些查询的问题在于它们需要的参数不是经典的 X = 'value' 形式。

例如,以下行崩溃

 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)

但是,以下查询有效:

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

(但它很容易被 SQL 注入...)

有谁知道setParameter()在这种情况下如何使用?

4

7 回答 7

83

对于本机查询,未定义命名参数的使用。从 JPA 规范(第3.6.3 节命名参数):

命名参数遵循第 4.4.1 节中定义的标识符规则。命名参数的使用适用于 Java Persistence 查询语言,并且未针对本机查询进行定义。 只有位置参数绑定可以可移植地用于本机查询

因此,请尝试以下操作:

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

请注意,在 JPA >= 2.0 中,您可以在本机查询中使用命名参数。

于 2010-06-29T22:35:17.310 回答
19

也许你可以更换

'POINT(:lon :lat)'

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

这样,参数在常量字符串之外,应该被查询解析​​器识别。

于 2010-06-29T20:50:10.947 回答
4

我遇到了类似的问题,发现可以在原生查询中用问号设置参数。尝试这个:

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);
于 2014-02-26T21:38:44.807 回答
2

Pascal 的回答是正确的,但是……您的解决方案如何容易发生 SQL 注入?如果您在示例中使用String.format和参数类型%f,则除数字之外的任何其他内容都会引发 java.util.IllegalFormatConversionException。没有像“xxx' OR 1=1 --”这样的可能通过值。

请注意,使用%sinString.format已准备好 SQL 注入。

于 2013-02-26T17:47:30.607 回答
2

你也可以摆脱整个

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

调用并将其替换为

ST_Point(:lon,:lat)

然后你不必担心报价。

于 2012-12-05T16:27:30.073 回答
2

因此,这个想法是使用 Jörn Horstmann 建议的连接技巧来强制 postgres 识别参数。以下代码有效:

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

非常感谢你的回答 !

于 2010-06-30T12:50:26.880 回答
2

我遇到了类似的问题。我在带有 ?1 的存储库中使用本机查询。它通过将参数括在括号中来解决它,如下所示。

SELECT * FROM XYZ WHERE ABC = (?1)

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

于 2015-03-16T20:44:01.463 回答