web-dev-qa-db-ja.com

JPA / Hibernate Native Queryはパラメーターを認識しません

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()の使用方法を知っている人はいますか?

45
user99054

名前付きパラメーターの使用は、ネイティブクエリには定義されていません。 JPA仕様(セクション.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以降では、ネイティブクエリの名前付きパラメータ使用可能に注意してください。

81
Pascal Thivent

たぶんあなたは交換することができます

'POINT(:lon :lat)'

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

このように、パラメータは定数文字列の外側にあり、クエリパーサーによって認識される必要があります。

17
Jörn Horstmann

同様の問題があり、ネイティブクエリで疑問符を使用してパラメーターを設定できることがわかりました。これを試して:

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

そのため、JörnHorstmannによって提案された連結トリックを使用して、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);

答えてくれてありがとう!

2
user99054

全体を取り除くこともできます

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

呼び出して置き換えます

ST_Point(:lon,:lat)

そうすれば、引用符について心配する必要はありません。

2
MDAHatter

同様の問題に直面しました。 ?1を使用して、リポジトリでネイティブクエリを使用していました。パラメータを次のように角かっこで囲むことで解決しました。

SELECT * FROM XYZ WHERE ABC = (?1)

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

2
yousafsajjad

パスカルの答えは正しいですが、...あなたのソリューションのSQLインジェクションはどのように起こりやすいですか?あなたの例でString.formatとparmater型%fを使用している場合、数値以外はJava.util.IllegalFormatConversionExceptionをスローします。 「xxx 'OR 1 = 1-」のような可能性のあるパス値はありません。

%sString.formatを使用すると、SQLインジェクションの準備ができていることに注意してください。

2
Boris Šuška