PreparedStatement 参数索引超出范围

Posted

技术标签:

【中文标题】PreparedStatement 参数索引超出范围【英文标题】:PreparedStatement parameter index out of range 【发布时间】:2020-08-10 13:25:20 【问题描述】:

在执行准备好的语句时出现参数索引超出范围错误。我还有其他几个正常工作的语句。此查询的唯一区别是它是唯一的 UPDATE。其余的都是 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,如果在控制台中执行,该语句可以正常工作。

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

编辑:这也是我正在使用的语句处理程序方法。我正在梳理,看看我还应该添加什么来帮助解决这个问题。谢谢你们的cmets!

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;
    

【问题讨论】:

看来StatementHandler.getPreparedStatement() 每次都返回的不是同一个实例,你能添加这个方法的代码吗?还要记住,如果 StatementHandler 被不同的线程调用,准备好的语句对象很可能在两个 getPreparedStatement() 调用之间被替换。 请提供minimal reproducible example。 【参考方案1】:

你的 sn-p 没有说清楚,但我可以猜到。我将列出我正在得出的一系列结论;您必须仔细检查这些:

    StatementHandler 是一个类(不是变量)。 (原因:你已经大写了)。 setPreparedStatement 和 getPreparedStatement 是 StatementHandler 类中的静态方法。 (自然而然)。 您正在使用多个线程(原因:这足以解释此问题)。 您没有同步(原因:与 #3 相同)。

那么这个结果很明显:你不能那样做。您的整个 VM 有一个全局“准备好的语句”,其中多个线程以或多或少的任意顺序调用 setPreparedStatement 和 getPreparedStatement。一个线程调用 setPreparedStatement,然后另一个线程调用,然后第一个线程尝试获取另一个设置的准备好的语句,这一切都交给了哈迪斯。

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

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

【讨论】:

【参考方案2】:

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

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

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

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

【讨论】:

以上是关于PreparedStatement 参数索引超出范围的主要内容,如果未能解决你的问题,请参考以下文章

参数索引超出范围(4 > 参数个数,即 3)

FLUTTER:RangeError(索引):索引超出范围:没有有效索引:0(通过参数)

指数超出范围。必须是非负数且小于集合的大小。 (参数'索引')'

错误:索引超出范围。必须是非负数且小于集合的大小。参数名称:索引

java.sql.SQLException:参数索引超出范围(2 > 参数个数,即 0)

错误“索引超出范围。必须为非负数且小于集合的大小。参数名称:索引”