通过 jdbc 通过准备好的语句传递 SQL 命令时出错

Posted

技术标签:

【中文标题】通过 jdbc 通过准备好的语句传递 SQL 命令时出错【英文标题】:Error in passing SQL command through a prepared statement via jdbc 【发布时间】:2012-06-06 21:00:40 【问题描述】:

我正在尝试创建程序在运行时获取行数和列数的表。

代码如下:

String sql = 
  "CREATE TABLE if not exists itemset (?";
up1 = con.prepareStatement(sql);
for(int j=1; j < ccolumns; j++) 
  sql += ",?";

sql += ")";
System.out.println(sql);
for(int j=1; j < ccolumns+1; j++) 
  System.out.println(j);
  up1.setString(j, "item"+j+" TINYINT(10)");

up1.executeQuery();

错误是

Parameter index out of range (2 &gt; number of parameters, which is 1)

在第二次迭代时出现在 setString 行中

【问题讨论】:

也许你应该调用“prepare”你完成构建 SQL 字符串 ;)? PreparedStatement 不应用于 CREATE TABLE sql。 【参考方案1】:

尝试移动

up1 = con.prepareStatement(sql); 

之后

System.out.println(sql);

【讨论】:

这确实解决了之前的问题,但现在它给出了一个新问题对于5的ccolumns输入,sql语句似乎已更改为''item1 TINYINT(10)','item2 TINYINT (10)','item3 TINYINT(10)','item4 TINYINT(10)',' 必须有 5 个而不是 4 个(第二个循环正在迭代 5 次)【参考方案2】:

虽然我没有验证,但我相信 Andriy 的回答是正确的。此外,您可能需要考虑使用StringBuilder 进行字符串累积,因为它们的重量较轻。下面更新了代码。

    StringBuilder sql = new StringBuilder("CREATE TABLE if not exists itemset (?");
    for (int j = 1; j < ccolumns; j++) 
        sql.append(",?");
    
    sql.append(")");
    System.out.println(sql.toString());
    up1 = con.prepareStatement(sql.toString());
    for (int j = 1; j < ccolumns + 1; j++) 
        System.out.println(j);
        up1.setString(j, "item" + j + " TINYINT(10)");
    
    up1.executeQuery();

【讨论】:

【参考方案3】:

您要修复的第一件事是,当您要更改它时,请不要使用String 实例。 String 实例是不可变的,当您修改已创建的 String 时,会创建新的修改实例并且它不是免费的。 您应该为此使用StringBuilder,它具有允许修改它而无需创建新实例的实例方法。我建议您在修改String 时始终使用StringBuilder。其次,您致电con.prepareStatement(sql),然后修改其sql字段。这不好不是吗? 所以只有在你使用 String 之后才添加它。

String sql = 
  "CREATE TABLE if not exists itemset (?";
for(int j=1; j < ccolumns; j++) 
  sql += ",?";

sql += ")";
System.out.println(sql);
up1 = con.prepareStatement(sql);
for(int j=1; j < ccolumns+1; j++) 
  System.out.println(j);
  up1.setString(j, "item"+j+" TINYINT(10)");

up1.executeQuery();

你不能用给定的String 调用prepareStatement 然后修改它。


但更清洁和更快尝试这样做

StringBuilder sqlStatement = new StringBuilder(
      "CREATE TABLE if not exists itemset (?");
    for(int j=1; j < ccolumns; j++) 
      sqlStatement.append(",?");
    
    sqlStatement.append(")");
    up1 = con.prepareStatement(sqlStatement.toString());
    for(int j=1; j < ccolumns+1; j++) 
      System.out.println(j);
      up1.setString(j, "item"+j+" TINYINT(10)");
    
    up1.executeQuery();

【讨论】:

@PhilippReichart 抱歉,我看到后立即更换错误,已更正。 错误是在字符串之前给出preparestatement,通过使用StringBuilder它仍然给出了我在回复andriy时提到的问题,无论如何我不知道StringBuilder谢谢【参考方案4】:

PreparedStatement 不能用于 DDL 查询,例如问题中的 CREATE TABLE

传递给setString() 的值在生成的 SQL 中被转义,导致发送到数据库的查询无效。

当只给出列数时,只需创建 CREATE TABLE 语句(如其他人建议的那样使用StringBuilder)并使用普通的Statement 执行它,不需要PreparedStatement

String getCreateTableSql(int columns) 
  StringBuilder sql = new StringBuilder("CREATE TABLE IF NOT EXISTS itemset (");
  for (int i = 0; i < columns; i++) 
    if (i > 0) 
      sql.append(", ");
    
    sql.append("item").append(i + 1).append(" TINYINT(10)");
  
  sql.append(")");
  return sql.toString();

对于 4 列,这将输出:

CREATE TABLE IF NOT EXISTS itemset (item1 TINYINT(10), item2 TINYINT(10), item3 TINYINT(10), item4 TINYINT(10))

【讨论】:

是的,简单的声明是有效的,但我想尝试准备声明。无论如何,谢谢

以上是关于通过 jdbc 通过准备好的语句传递 SQL 命令时出错的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 jdbc 准备好的语句传递可变数量的参数?

JDBC驱动程序为准备好的语句转义参数?

使用准备好的语句查询列表 (JDBC)

当它们包含 ROW_NUMBER() 时,JDBC-driver / SQL Server 总是重新编译我准备好的语句

mssql-jdbc MS SQL Server JDBC 驱动程序准备好的语句缓存性能问题与 Hikari CP

使用 MySQL 和 JDBC 准备语句缓存