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

时间:2009-11-28 21:41:25

标签: java sqlite exception jdbc resultset

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

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

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

如果没有先前的JDBC经验,我的理论(我无法通过JavaDocs确认ResultSet)是

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

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

我的要求是:

  1. 上述理论是否正确?
  2. 这是一个错误吗?特征? (如果是的话,有人可以指向文件吗?)
  3. 是否特定于SQLIte的JDBC或所有JDBC驱动程序中的通用ResultSet
  4. 这样做的正确方法是什么?
  5. 对于#4,我的解决方案是在isFirst()之后立即使用executeQuery()调用来检查结果集中是否有任何行。这是最佳做法吗?

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

    谢谢!

4 个答案:

答案 0 :(得分:17)

是否清空,但执行以下操作始终是错误

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;
}

然后在business / domain对象中相应地处理它,例如

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;
}

然后在business / domain对象中相应地处理它,例如

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

if (!users.isEmpty()) {
    int count = users.size();
    // ...
}
else {
    // Help, no users?
}

答案 1 :(得分:6)

while (rs.next()) {
 // process the row
}

答案 2 :(得分:5)

来自ResultSet的JavaDocs:

  

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

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

答案 3 :(得分:1)

对于这类问题,您可以使用

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);