web-dev-qa-db-ja.com

JDBCプリペアドステートメントのリテラル疑問符( '?')をエスケープするにはどうすればよいですか?

次のようなJDBCPreparedStatementを作成したいと思います。

_SELECT URL,LOCATE ( '?', URL ) pos FROM Links WHERE pageId=? ORDER BY pos ASC
_

ここで、1番目の_?_はリテラルで、2番目の_?_はパラメーターです。 _'?'_の代わりにCHAR(63)を使用することもできますが、余分な関数呼び出しによってSQLの実行が遅くなると思います。その最初の_?_を回避する方法はありますか?

編集:

次のコードは、文字列内の_?_文字がマーカーと見なされないというdkatzelの主張をテストします。

_public class Test {
    public static void main(String[] args) throws SQLException {
        Connection conn = DriverManager.getConnection("jdbc:h2:mem:test");
        Statement stmt = conn.createStatement();
        stmt.executeUpdate("CREATE TABLE Links(URL VARCHAR(255) PRIMARY KEY,pageId BIGINT)");
        stmt.executeUpdate("INSERT INTO Links(URL,pageId) VALUES('http://foo.bar?baz',1)");
        stmt.executeUpdate("INSERT INTO Links(URL,pageId) VALUES('http://foo.bar/baz',1)");
        stmt.close();
        PreparedStatement ps = conn
            .prepareStatement("SELECT URL,LOCATE ( '?', URL ) pos FROM Links WHERE pageId=? ORDER BY pos ASC");
         ps.setLong(1, 1);
        ResultSet rs = ps.executeQuery();
        while (rs.next()) {
            System.out.println(rs.getString(1) + ":" + rs.getInt(2));
        }
        rs.close();
        ps.close();
        conn.close();
    }
}
_

出力:

_http://foo.bar/baz:0
http://foo.bar?baz:15
_

Dkatzelは正しいようです。 JDBC Spec を検索しましたが、_?_パラメーターマーカーが引用符で囲まれている場合は無視されるという言及は見つかりませんでしたが、PreparedStatementパーサーのいくつかの実装が見つかりました( MySqlc-JDBCH2 )はすべて、パラメーターマーカーとしての考慮から一重引用符内のテキストを除外しているように見えます。

12
Mike Godin

?の意味はSQL仕様で指定されており、JDBC仕様はこのためのSQL仕様に従います。

文字列リテラル内の疑問符は単に文字列リテラル内の文字であるため、ドライバはリテラル内の疑問符をパラメータプレースホルダーとして解釈しません(また、解釈すべきではありません)。詳細については、SQL:2011 Foundation(ISO-9075-2:2011)の第5章を参照してください。

したがって、エスケープする必要はありません(不可能です)。

4
Mark Rotteveel

使用しているJDBCドライバーによっては、別の疑問符を追加することでエスケープできる場合があります。 PostgreSQLを使用している場合

https://jdbc.postgresql.org/documentation/head/statement.html

JDBCでは、疑問符(?)は、PreparedStatementの位置パラメーターのプレースホルダーです。ただし、疑問符を含むPostgreSQL演算子がいくつかあります。 SQLステートメント内のこのような疑問符が位置パラメーターとして解釈されないようにするには、2つの疑問符(??)エスケープシーケンスとして。このエスケープシーケンスをステートメントで使用することもできますが、必須ではありません。具体的には、ステートメント内で1つだけ(?)演算子として使用できます。

16
bobmarksie

JDBCドライバーで機能しない場合は、String?としてバインドできます。

ps.setString(1, "?");
7
Elliott Frisch

やってみましたか?引用された疑問符は大丈夫だと思います。準備されたステートメントでは、「裸の」疑問符のみを置き換える必要があります

3
dkatzel

クエリでCHR(63)を使用しましたが、これは問題の解決に役立ちました。

これが私が例えばしたことです:select q'[<div id=['|"]TRD_%%GEN%%['|"].*]' || chr(63) || q'[</div>]' from dual;

これは、文字列を次のように取得するのに役立ちました:<div id=['|"]TRD_%%GEN%%['|"].*?</div>

次に、このクエリを挿入ステートメント内で使用し、PreparedStatementを実行しました。完全にうまくいきました。

CHR関数は組み込み関数であり、他のOracle関数と同様に使用できます。クエリが何度も繰り返されないことがわかっている場合は、これを使用できます。

2