JDBC 驱动程序在空 ResultSet 上抛出“ResultSet Closed”异常

Posted

技术标签:

【中文标题】JDBC 驱动程序在空 ResultSet 上抛出“ResultSet Closed”异常【英文标题】:JDBC driver throws "ResultSet Closed" exception on empty ResultSet 【发布时间】:2010-12-21 07:04:44 【问题描述】:

我在 SQLite 的 JDBC 驱动程序中遇到问题。

我正在使用 SELECT 语句执行查询。

如果我得到一个空的 ResultSet(0 行),那么我会在调用 getString(1) 时看到一个“Closed ResultSet”异常。

在没有太多 JDBC 经验的情况下,我的理论(我无法通过 ResultSet 的 JavaDocs 确认)是这样的

getString(1) 不适用于空(零行)结果集(根据设计或由于错误) ResultSet 的“open”标志在零行上设置为 false(同样,由于设计或错误)

我看到了这个bug report,但不确定它是否相关。

我的问题是:

    上述理论正确吗? 这是一个错误吗?特征? (如果是这样,有人可以指出文档吗?) 它是特定于 SQLIte 的 JDBC 还是所有 JDBC 驱动程序中的通用 ResultSet这样做的正确方法是什么?

对于#4,我的解决方案是在executeQuery() 之后使用isFirst() 调用来检查结果集中是否存在任何行。这是最佳实践方法吗?

(我也可以简单地选择一个计数 insetad,因为我真的不需要结果集,只需要零非零标志,但如果我确实关心 select 的结果,我想知道正确的做法)

谢谢!

【问题讨论】:

谢谢大家!非常有启发性! 【参考方案1】:
while (rs.next()) 
 // process the row

【讨论】:

这回答了#4。 #1-3 怎么样? :)【参考方案2】:

来自ResultSet 的 JavaDocs:

ResultSet 对象维护一个游标 指向其当前的数据行。 最初定位光标 在第一行之前。下一个方法 将光标移动到下一行,并且 因为它在那里返回false ResultSet 中不再有行 对象,它可以在while循环中使用 遍历结果集。

您需要将ResultSet 定位在一行中,例如在尝试读取任何数据之前调用next()。如果对next() 的调用返回false,则结果集为空。

【讨论】:

这回答了#4。 #1-3 怎么样? :) @DVK 在ResultSet 位于一行之前,调用getString 是没有意义的,因此引发异常是有意义的(这适用于任何JDBC 驱动程序) .我不确定“结果集的“打开”标志”指的是什么。 在 Eclipse 的调试器中,当检查结果集时,“open”成员的值为“false”。这就是“ResultSet closed”异常的原因(不是根本原因,而是在 JDBC 源代码中检查生成异常的直接变量)【参考方案3】:

是否为空,但执行以下操作总是错误

resultSet = statement.executeQuery(sql);
string = resultSet.getString(1); // Epic fail. The cursor isn't set yet.

这不是错误。这是documented behaviour。每个decent JDBC tutorial 都会提到它。您需要使用next() 设置ResultSet 的光标,然后才能访问任何数据。

如果您真的对所谓的唯一行是否存在感兴趣,那么只需检查next() 的结果。例如在一个虚构的UserDAO 类中:

public boolean exist(String username, String password) throws SQLException 
    boolean exist = false;

    try (
        Connection connection = database.getConnection();
        PreparedStatement statement = connection.prepareStatement("SELECT id FROM user WHERE username = ? AND password = MD5(?)");
    ) 
        statement.setString(1, username);
        statement.setString(2, password);

        try (ResultSet resultSet = statement.executeQuery()) 
            exist = resultSet.next();
        
    

    return exist;

如果您实际上只期望 一个 行,那么只需执行以下操作:

public User find(String username, String password) throws SQLException 
    User user = null;

    try (
        Connection connection = database.getConnection();
        PreparedStatement statement = connection.prepareStatement("SELECT id, username, email, birthdate FROM user WHERE username = ? AND password = MD5(?)");
    ) 
        statement.setString(1, username);
        statement.setString(2, password);

        try (resultSet = statement.executeQuery()) 
            if (resultSet.next()) 
                user = new User(
                    resultSet.getLong("id"),
                    resultSet.getString("username"),
                    resultSet.getString("email"),
                    resultSet.getDate("birthdate")); 
            
        
    

    return user;

然后在业务/域对象中相应地处理它,例如

User user = userDAO.find(username, password);

if (user != null) 
    // Login?

else 
    // Show error?

如果您实际上只期望 许多 行,那么只需执行以下操作:

public List<User> list() throws SQLException 
    List<User> users = new ArrayList<User>();

    try (
        Connection connection = database.getConnection();
        PreparedStatement statement = connection.prepareStatement("SELECT id, username, email, birthdate FROM user");
        ResultSet resultSet = statement.executeQuery();
    ) 
        while (resultSet.next()) 
            users.add(new User(
                resultSet.getLong("id"),
                resultSet.getString("username"),
                resultSet.getString("email"),
                resultSet.getDate("birthdate")));
        
    

    return users;

然后在业务/域对象中相应地处理它,例如

List<User> users = userDAO.list();

if (!users.isEmpty()) 
    int count = users.size();
    // ...

else 
    // Help, no users?

【讨论】:

由于代码示例和文档链接的广泛性,我选择“接受”这个。【参考方案4】:

对于这样的问题,你可能会这样使用

while(rs.next())

    String name=rs.getString("Name");
    int roll_no=Integer.parseInt(rs.getString("Roll"));

finally

    try
    
        rs.close();
        pst.close();
    
    catch(Exception ee)
    
    


Joptionpane.showmessahedialog(this,ee);

【讨论】:

注意格式化帖子。我已经格式化了您的一个答案,请检查并格式化其他答案(如果有)! thanx Paresh Mayani 我是这个网站的新手。 不用担心...继续贡献!

以上是关于JDBC 驱动程序在空 ResultSet 上抛出“ResultSet Closed”异常的主要内容,如果未能解决你的问题,请参考以下文章

尝试使用 H2 数据库更新 JDBC ResultSet 时出现异常

django makemigration 在子应用程序上抛出错误

glCreateShader 在 OSX 上抛出异常,为啥?

HSQLDB 在 Windows 上运行,但在 Linux 上抛出异常

Nginx 在静态文件上抛出 403 Forbidden

jdbc数据库连接在方法中,而且要返回statement 或resultset 在方法里关闭连接会怎么样?要怎么处理?