PreparedStatement 参数索引超出范围

PreparedStatement parameter index out of range

我在执行准备好的语句时收到参数索引超出范围的错误。我有其他几个语句正常工作。此查询的唯一区别是它是唯一的更新。其余的都是 INSERT、ADD、DELETE 等。非常感谢任何关于我可能做错的指导。

sqlStatement = "UPDATE customer SET customerName = ?, addressId = ? WHERE customerId = ?;";

StatementHandler.setPreparedStatement(ConnectionHandler.connection, sqlStatement);

StatementHandler.getPreparedStatement().setString(1, name);
StatementHandler.getPreparedStatement().setInt(2, AddressDAO.getAddressId(address));
StatementHandler.getPreparedStatement().setInt(3, customerId);
StatementHandler.getPreparedStatement().executeUpdate();

错误:

java.sql.SQLException: Parameter index out of range (3 > number of parameters, which is 1).

我在代码块中间放了几个打印语句,它似乎在第三个参数上失败了。所有传入的值都是有效的,并且与分配的类型相匹配。 MySQL 正在使用,如果在控制台中执行,该语句工作正常。

感谢您的阅读以及您能提供的任何帮助。

编辑:这也是我正在使用的语句处理程序方法。我正在梳理,看看我还应该添加什么来帮助弄清楚这件事。感谢您的评论!

public class StatementHandler {

    /**
     * create statement reference
     */
    private static PreparedStatement preparedStatement;

    /**
     * method to create statement object
     */
    public static void setPreparedStatement(Connection connection, String sqlStatement) throws SQLException {
        preparedStatement = connection.prepareStatement(sqlStatement);
    }

    /**
     * getter to return statement object
     */
    public static PreparedStatement getPreparedStatement(){
        return preparedStatement;
    }
}

你的代码片段没有说清楚,但我可以猜到。我将列出我得出的一系列结论;你必须仔细检查这些:

  1. StatementHandler 是一个 class(不是变量)。 (原因:你已经大写了)。
  2. setPreparedStatement 和 getPreparedStatement 是 StatementHandler class 中的静态方法。 (顺其自然)
  3. 您正在使用多线程(原因:这足以说明这个问题)。
  4. 您没有同步(原因:与 #3 相同)。

那么这个结果很明显:你不能那样做。您的整个 VM 有一个全局 'prepared statement',多个线程以或多或少的任意顺序调用 setPreparedStatement 和 getPreparedStatement。一个线程调用 setPreparedStatement,然后另一个线程执行,然后第一个线程尝试获取另一组的准备好的语句,然后全部转到 handbasket 中的 hades。

你不能这样做。哎呀,你甚至不能在两个线程之间共享连接(因为它们会互相妨碍并搞乱你的事务)。

如果您不太了解 static 的作用(诚然,这是一个高级主题),请不要使用它。您几乎可以在不使用静态方法的情况下编写所有您想要的 java 。一个例外是 public static void main,它必须是静态的,但只需将其设为 one-liner: new MyClass().go();,其中 go() 是一个 non-static 方法,您就可以去吧。

我想比 rzwitserloot 更进一步,并假设您的 AddressDAO 也使用 StatementHandler。

AddressDAO.getAddressId(address)的查询可能有一个参数,它匹配Exception中的1,并在设置第三个参数之前替换prepredStatemt。

作为证明,在设置准备好的语句之前将 AddressDAO.getAddressId(address) 分配给一个变量(并在之后使用它)就足够了。

或者,您可以在变量中获取一次准备好的语句,然后再使用该变量。