对于某些sql语句,我不能使用准备好的语句,例如:
SELECT MAX(AGE) FROM ?
例如,当我想改变表格时。是否有一个实用程序可以在Java中清理sql?红宝石中有一个。
答案 0 :(得分:17)
是的,准备好的语句查询参数只能在使用单个文字值的地方使用。您不能将参数用于表名,列名,值列表或任何其他SQL语法。
因此,您必须将应用程序变量插入到SQL字符串中并适当地引用字符串。使用引用来分隔您的表名称标识符,并通过加倍来转义引号字符串:
java.sql.DatabaseMetaData md = conn.getMetaData();
String q = md.getIdentifierQuoteString();
String sql = "SELECT MAX(AGE) FROM %s%s%s";
sql = String.format(sql, q, tablename.replaceAll(q, q+q), q);
例如,如果您的表名字面上是table"name
,并且您的RDBMS标识符引号字符是"
,那么sql
应该包含如下字符串:
SELECT MAX(AGE) FROM "table""name"
我也同意@ChssPly76的评论 - 如果您的用户输入实际上不是文字表名,而是表示您的代码映射到表名,然后插入到SQL查询中。这可以让您更加确信不会发生SQL注入。
HashMap h = new HashMap<String,String>();
/* user-friendly table name maps to actual, ugly table name */
h.put("accounts", "tbl_accounts123");
userTablename = ... /* user input */
if (h.containsKey(userTablename)) {
tablename = h.get(userTablename);
} else {
throw ... /* Exception that user input is invalid */
}
String sql = "SELECT MAX(AGE) FROM %s";
/* we know the table names are safe because we wrote them */
sql = String.format(sql, tablename);
答案 1 :(得分:2)
不可能。最好的办法是使用String#format()
。
String sql = "SELECT MAX(AGE) FROM %s";
sql = String.format(sql, tablename);
请注意,这不会避免SQL注入风险。如果tablename
是用户/客户端控制的值,则需要使用String#replaceAll()
对其进行清理。
tablename = tablename.replaceAll("[^\\w]", "");
希望这有帮助。
[编辑]我应该添加:不要将此用于可以使用PreparedStatement
的列值。只需继续使用任何列值的常用方法。
[Edit2]最好不要让用户/客户端能够以他们想要的方式输入表名,但最好提供一个下拉列表,其中包含所有有效的表名(可以通过DatabaseMetaData#getCatalogs()
获取) UI,以便用户/客户端可以选择它。如果选择有效,请不要忘记检查服务器端,因为可能会欺骗请求参数。
答案 2 :(得分:0)
在这种情况下,您可以通过从DatabaseMetaData获取表列表,根据可用表列表验证表名。实际上,使用正则表达式去除空格可能更容易一些,也许还有一些sql保留字,“;”等,在使用一些谎言String.format之前从字符串构建完整的sql语句。
你不能使用preparedStatement的原因是因为它可能将表名包含在's中并像字符串一样转义它。