Java PreparedStatement SQL语法错误

时间:2016-03-24 02:28:41

标签: java mysql jdbc

这是一个非常奇怪的错误,只是今天才开始出现。当我使用准备好的声明时?对于参数,我得到一个错误,但是当我使用它而没有参数时,它工作得很好。 这是导致错误的代码:

    String table = "files";
    Connection conn = DriverManager.getConnection(DB_URL, DB_USER, DB_PASS);
    PreparedStatement prep = conn.prepareStatement("SELECT * FROM ?");
    prep.setString(1, table);
    ResultSet rs = prep.executeQuery();

    while(rs.next()) {
        System.out.println(rs.getString("file_name"));
    }

这会产生以下错误:

Exception in thread "main" com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ''files'' at line 1

此外,将其更改为以下内容的工作正常:

    String table = "files";
    Connection conn = DriverManager.getConnection(DB_URL, DB_USER, DB_PASS);
    PreparedStatement prep = conn.prepareStatement("SELECT * FROM " + table);
    ResultSet rs = prep.executeQuery();

    while(rs.next()) {
        System.out.println(rs.getString("file_name"));
    }

这似乎没有多大意义。有什么想法吗?

<小时/> 在另一张桌子上试了一下,得到了更多的结果。 这可以正常运行并记录管理员:

    String sql = "SELECT * FROM " + ADMIN_AUTH_TABLE + " WHERE " + column + " = '" + hashedPassword + "'";
    PreparedStatement prepared = connection.prepareStatement(sql);

以下不会导致错误,但会返回一条消息,说明输入的密码不正确(这是正确的 - 我加倍检查)。

    String sql = "SELECT * FROM " + ADMIN_AUTH_TABLE + " WHERE ? = ?";
    PreparedStatement prepared = connection.prepareStatement(sql);
    prepared.setString(1, column);
    prepared.setString(2, hashedPassword);

<小时/> 知道了:用吗?对于价值观。 此外,the answer here也有帮助。

2 个答案:

答案 0 :(得分:2)

绑定参数不能用于SQL语句中的标识符。只有可以通过绑定占位符提供。

这将有效:

SELECT foo FROM bar WHERE id = ? 

这将工作,因为表名是标识符

SELECT foo FROM ? WHERE id = 2

您无法提供列名,因为列名也是标识符。 这样的声明会运行,但它可能无法完成您的想法。

SELECT ? AS foo FROM bar WHERE ? = 0

如果我们为两个占位符提供'foo'的值,则查询实际上等同于包含两个字符串文字的查询:

SELECT 'foo' AS foo FROM bar WHERE 'foo' = 0

MySQL会运行该语句,因为它是一个有效的语句(如果表栏存在且我们对它有特权。)该查询将返回bar中的每一行(因为WHERE子句中的谓词的计算结果为TRUE,与表的内容..然后我们返回常量字符串foo

字符串foo碰巧与表格中的列名相匹配并不重要。

此限制与SQL优化器的运行方式有关。我们不需要深入研究这些步骤的所有细节(简要地说:解析令牌,执行语法检查,执行语义检查,确定查询计划,然后实际执行查询计划。)

所以这是一个简短的故事:绑定参数的值在该过程中提供得太晚。在最后一步执行查询计划之前,它们不会被提供。

优化器需要知道在早期阶段引用了哪些表和列...用于语义检查,以及用于开发查询计划。表和列必须标识到优化器。在需要表名和列名时,绑定占位符是“未知数”。

(这个简短的故事并不完全准确;不要将所有这些都视为福音。但它确实解释了绑定参数不能用于标识符的原因,如表名和列名。)

<强> TL;博士

鉴于您正在运行的特定语句,可以作为绑定参数传入的唯一将是“hashedPassword”值。该语句中的其他所有内容都必须在SQL字符串中。

例如,像这样的东西可以起作用:

String sqltext = "SELECT * FROM mytable WHERE mycolumn = ?";
PreparedStatement prepared = connection.prepareStatement(sqltext);
prepared.setString(1, hashedPassword);

要使SQL语句的其他部分“动态”(如表名和列名),您必须在Java代码中处理它(使用字符串连接。)该字符串的内容需要结束就像传递给prepareStatement方法时sqltext字符串(在我的例子中)的内容一样。

答案 1 :(得分:1)

PreparedStatement的参数应仅应用于可在条件子句中使用的参数。表名不是这里的情况。

如果您有一个选择表格可以在条件子句中应用,您可以这样做,否则你不能。