如何在JDBC预处理语句中转义文字问号('?')

时间:2014-10-22 20:08:50

标签: java sql jdbc prepared-statement

我想创建一个JDBC PreparedStatement,如:

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

第一个?是文字,第二个?是参数。我可以使用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解析器的几个实现(MySql,{ {3}},c-JDBC)似乎都将单引号中的文本排除在考虑范围之外作为参数标记。

5 个答案:

答案 0 :(得分:10)

根据您使用的JDBC驱动程序,您可以通过添加其他问号来逃避,例如:如果你正在使用PostgreSQL

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

  

在JDBC中,问号(?)是PreparedStatement的位置参数的占位符。但是,有许多PostgreSQL运算符包含问号。要使SQL语句中的此类问号不被解释为位置参数,请使用两个问号(??)作为转义序列。您也可以在Statement中使用此转义序列,但这不是必需的。特别是在语句中,单个(?)可以用作运算符。

答案 1 :(得分:6)

如果它不能与您的JDBC驱动程序一起使用,您可以将其绑定为String ?

ps.setString(1, "?");

答案 2 :(得分:4)

?的含义在SQL规范中指定,JDBC规范遵循SQL规范。

驱动程序不会(并且不应该)将文字中的问号解释为参数占位符,因为字符串文字中的问号只是字符串文字中的字符。有关更多信息,请参阅SQL:2011 Foundation(ISO-9075-2:2011)的第5章。

因此没有必要(也不可能)逃避。

答案 3 :(得分:3)

你尝试过吗?我认为引用的问号是可以的。在准备好的陈述中只应替换“裸”问号

答案 4 :(得分:2)

我在查询中使用了CHR(63),这有助于解决我的问题。

以下是我所做的例子: $http.get(api.getUrl('latestActivityByTeamAndModule', [$scope.team_id, $scope.module_id])) .success(function(response){ var i = 0; $scope.line = []; $scope.categories = []; response.forEach(function(y){ var log_date = y.date.substr(0, y.date.indexOf('T')); var date = new Date(log_date); var logg_date = moment(date).fromNow(); $scope.categories.push(logg_date); $scope.line.push(y.num_taken); }); });

这有助于获取字符串: <div id="test" ui-jq="plot" ui-options="[ { data: [4], points: { show: true, radius: 6}, splines: { show: true, tension: 0.45, lineWidth: 5, fill: 0 }, label: 'Akademi' }, ], { colors: ['#23b7e5', '#27c24c'], series: { shadowSize: 3 }, xaxis:{ font: { color: '#ccc' }, position: 'bottom', ticks: [&quot;21 hours ago&quot;] }, yaxis:{ font: { color: '#ccc' } }, grid: { hoverable: true, clickable: true, borderWidth: 0, color: '#ccc' }, tooltip: true, tooltipOpts: { content: '%x.1 is %y.4', defaultTheme: false, shifts: { x: 0, y: 20 } }, redrawOverlayInterval: 60 }" style="height: 240px; padding: 0px; position: relative;">

然后我在insert语句中使用了这个查询,并运行了PreparedStatement。工作得非常好。

CHR函数是一个内置函数,可以像其他oracle函数一样使用。如果您知道查询将不会重复多次,您可以使用它。