【问题标题】:Java prepared statement in try-with-resources not working [duplicate]java在try-with-resources中准备的语句不起作用[重复]
【发布时间】:2014-09-15 22:09:04
【问题描述】:

昨天 Stack 上的多个人推荐使用 try-with-resources。我现在正在为我的所有数据库操作执行此操作。今天我想将 Statement 更改为 PreparedStatement 以使查询更安全。但是,当我尝试在 try-with-resources 中使用准备好的语句时,我不断收到诸如“预期标识符”或“;”之类的错误。或')'。

我做错了什么?或者这不可能吗?这是我的代码:

    try (Connection conn = DriverManager.getConnection(DBURL, DBUSER, DBPASS);
        PreparedStatement stmt = conn.prepareStatement("SELECT id FROM users WHERE id = ? LIMIT 1");
        stmt.setInt(1, user);
        ResultSet rs = stmt.executeQuery()) {

        // if no record found
        if(!rs.isBeforeFirst()) {
           return false;
        }
        // if record found
        else {
            return true;
        }

    } catch (SQLException e) {
        // log error but dont do anything, maybe later
        String error = "SQLException: " + e.getMessage() + "\nSQLState: " + e.getSQLState() + "\nVendorError: " + e.getErrorCode();
        return false;

    }

【问题讨论】:

  • 在第一行,将; 更改为) {。 (愚蠢的错字?)

标签: java sql prepared-statement


【解决方案1】:

try-with-resource 语句用于声明 (Autoclosable) 资源。 ConnectionPreparedStatementResultSetAutoclosable,没关系。

但是stmt.setInt(1, user) 不是资源,而是一个简单的语句。在 try-with-resource 语句中不能有简单的语句(不是资源声明)!

解决方案:创建多个 try-with-resource 语句!

try (Connection conn = DriverManager.getConnection(DBURL, DBUSER, DBPASS)) {
    executeStatement(conn);
} catch (SQLException e) {
    // log error but dont do anything, maybe later
    String error = "SQLException: " + e.getMessage() + "\nSQLState: " + e.getSQLState() + "\nVendorError: " + e.getErrorCode();
    return false;
}

private void executeStatement(Connection con) throws SQLException {
    try (PreparedStatement stmt = conn.prepareStatement("SELECT id FROM users WHERE id=? LIMIT 1")) {
        stmt.setInt(1, user);
        try (ResultSet rs = stmt.executeQuery()) {
            // process result
        }
    }
}

(请注意,从技术上讲,不需要像我那样将 SQL 语句的执行放入单独的方法中。如果打开连接和创建 PreparedStatement 都在同一个 try-with 中,它也可以工作-resource 声明。我只是认为将连接管理内容与其余代码分开是一种好习惯。

【讨论】:

  • 为什么?您可以将ConnectionPreparedStatement 放在同一个资源创建块中,然后使用stmt
  • 没错,但通常在现实世界的应用程序中打开 JDBC 连接和执行 SQL 工作是/应该分开的。当然,您也可以在一个方法中拥有所有内容。
  • 但是谢谢你的评论。将其添加到我的答案中以避免混淆。
【解决方案2】:

试试这个代码:

try (Connection conn = DriverManager.getConnection(DBURL, DBUSER, DBPASS)) {
     PreparedStatement stmt = conn.prepareStatement("SELECT id FROM users WHERE id = ? LIMIT 1");

        stmt.setInt(1, user);
        ResultSet rs = pstmt.executeQuery())

        // if no record found
        if(!rs.isBeforeFirst()) {
           return false;
        }
        // if record found
        else {
            return true;
        }

    } catch (SQLException e) {
        // log error but dont do anything, maybe later
        String error = "SQLException: " + e.getMessage() + "\nSQLState: " + e.getSQLState() + "\nVendorError: " + e.getErrorCode();
        return false;

    }

请注意,这里的资源是您的连接,您必须在 try 块中使用它()

【讨论】:

  • 真的,您应该在资源创建块中也有PreparedStatement,以便正确关闭它。
  • 好的,同意。另外,在这里找到关于同一主题的帖子:stackoverflow.com/questions/8066501/… 可能有人可以将此标记为重复。我还没有那个特权。
  • 然而,该帖子并未涵盖此特定问题。
  • @Ross 我的意思是将问题标记为重复。这个问题是在询问如何正确使用 JDBC 的 try-with-resources,就像我发布的链接一样。
  • 另外,抛出异常时ResultSet不会关闭。
【解决方案3】:

移动

stmt.setInt(1, user);
ResultSet rs = stmt.executeQuery()

...在try{ /*HERE*/ }

这是因为stmt 是正在创建的资源try (/*HERE*/) {} 将被使用try{ /*HERE*/ }

试用资源

try (/*Create resources in here such as conn and stmt*/)
{
  //Use the resources created above such as stmt
}

重点是在资源创建块implements AutoClosable 中创建的所有内容,当退出try 块时,它们都会调用close()。 在您的代码中,stmt.setInt(1, user); 不是 AutoCloseable 资源,因此存在问题。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-05-21
    • 2021-02-14
    • 1970-01-01
    • 2015-04-26
    • 1970-01-01
    • 2014-12-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多