Драйвер JDBC выдает исключение ResultSet Closed для пустого ResultSet - PullRequest
9 голосов
/ 29 ноября 2009

У меня проблема в драйвере JDBC для SQLite.

Я выполняю запрос с оператором SELECT.

Если я получаю пустое ResultSet (0 строк), то при вызове getString(1).

появляется исключение "Закрытый ResultSet".

Без большого опыта JDBC моя теория (которую я не смог подтвердить с помощью JavaDocs для ResultSet) состоит в том, что

  • getString(1) НЕ работает с пустым (нулевая строка) набором результатов (по замыслу или из-за ошибки)
  • 1016 * флаг "открытия" установлен на false в нулевых строках (опять же, по замыслу или из-за ошибки)

Я видел это сообщение об ошибке , но не уверен, что это связано.

Мои вопросы:

  1. Верна ли теория выше?
  2. Это ошибка? Особенность? (и если да, может кто-нибудь указать на документацию, пожалуйста?)
  3. Это специфично для JDBC SQLIte или для общего ResultSet во всех драйверах JDBC?
  4. Как правильно делать такие вещи? ?

Для # 4 моим решением было использовать вызов isFirst() сразу после executeQuery(), чтобы проверить, есть ли какие-либо строки в наборе результатов. Это лучший метод?

(Я мог бы также просто выбрать инстадию счета, так как мне не нужен набор результатов, просто флаг, отличный от нуля, но я хочу знать, что нужно делать, если мне не безразличны результаты select)

Спасибо!

Ответы [ 4 ]

17 голосов
/ 29 ноября 2009

Пусто или нет, но выполнение следующих действий всегда неисправно :

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

Это не ошибка. Это задокументированное поведение . Каждый приличный учебник по JDBC упоминает об этом. Вам нужно установить курсор ResultSet с помощью next(), прежде чем вы сможете получить доступ к любым данным.

Если вам действительно интересно, существует ли предположительно уникальная строка или нет, просто проверьте результат 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?
}
6 голосов
/ 29 ноября 2009
while (rs.next()) {
 // process the row
}
5 голосов
/ 29 ноября 2009

Из JavaDocs для ResultSet :

Объект ResultSet поддерживает курсор указывая на его текущий ряд данных. Первоначально курсор расположен до первого ряда. Следующий метод перемещает курсор в следующую строку и потому что он возвращает ложь, когда есть больше нет строк в ResultSet объект, он может быть использован в цикле while перебрать набор результатов.

Вам нужно расположить ResultSet в строке, например, позвонив по номеру next(), прежде чем пытаться прочитать какие-либо данные. Если вызов next() возвращает false, тогда результирующий набор будет пустым.

1 голос
/ 07 мая 2015

Для такого рода проблем вы можете использовать вот так

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