这是一个非常奇怪的错误,只是今天才开始出现。当我使用准备好的声明时?对于参数,我得到一个错误,但是当我使用它而没有参数时,它工作得很好。 这是导致错误的代码:
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也有帮助。
答案 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
的参数应仅应用于可在条件子句中使用的参数。表名不是这里的情况。
如果您有一个选择表格可以在条件子句中应用,您可以这样做,否则你不能。