2009-11-28 7 views
9

J'ai un problème dans le pilote JDBC pour SQLite.Le pilote JDBC lève l'exception "ResultSet Closed" sur vide ResultSet

J'exécute une requête avec l'instruction SELECT.

Si je reçois un vide ResultSet (0 lignes) alors je vois une exception "Closed ResultSet" levée lors de l'appel getString(1).

Sans expérience JDBC bien avant, ma théorie (que je ne pouvais pas confirmer par JavaDocs pour ResultSet) est que

  • getString(1) ne fonctionne pas sur un vide (zéro ligne) resultset (par la conception ou en raison de un bug)
  • drapeau « ouvert » de ResultSet est réglé sur false sur zéro lignes (encore une fois, par la conception ou un bug)

j'ai vu ce bug report mais ne suis pas sûr que ce soit lié .

Mes qeustions sont:

  1. Est-ce la théorie ci-dessus correcte?
  2. Est-ce un bug? Fonctionnalité? (et si oui, quelqu'un peut-il pointer vers la documentation s'il vous plaît?)
  3. Est-il spécifique à JDBC de SQLIte ou à générique ResultSet dans tous les pilotes JDBC?
  4. Quelle est la bonne façon de faire des choses comme ça??

Pour # 4, ma solution était d'utiliser isFirst() appel juste après executeQuery() pour vérifier si toutes les lignes sont là dans le jeu de résultats. Est-ce l'approche des meilleures pratiques?

(Je pourrais aussi avoir simplement sélectionné un compte insetad puisque je n'avais pas vraiment besoin d'un ensemble de résultats, simplement zéro-non nul, mais je veux savoir ce que je devrais faire si je faisais attention aux résultats de select)

Merci!

+1

Merci à tous! Très éclairant! – DVK

Répondre

17

vide ou non, mais faire ce qui suit est toujours défectueux:

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

Ce n'est pas un bug. C'est documented behaviour. Chaque decent JDBC tutorial le mentionne. Vous devez définir le curseur du ResultSet en utilisant next() avant de pouvoir accéder à des données.

Si vous êtes réellement intéressé si la ligne soi-disant unique existe ou pas, il suffit de vérifier le résultat de next(). Par exemple, dans une classe UserDAO fictive:

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

Si vous attendez réellement que zéro ou une rangée, puis juste faire quelque chose comme:

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

et gérer simplement en conséquence dans la objet métier/domaine, par exemple

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

if (user != null) { 
    // Login? 
} 
else { 
    // Show error? 
} 

Si vous attendez réellement seulement zéro ou beaucoup lignes, puis il suffit de faire quelque chose comme:

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

et poignée juste en conséquence dans l'objet métier/domaine, par exemple

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

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

Je choisis de "Accepter" celui-ci en raison de l'ampleur des exemples de code et les liens de doc. – DVK

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

Cette réponse # 4. Quel abiout # 1-3? :) – DVK

5

De l'JavaDocs pour ResultSet:

Un objet ResultSet maintient un curseur pointant vers sa ligne courante de données. Initialement, le curseur est positionné avant la première ligne. La méthode suivante déplace le curseur sur la ligne suivante et car elle renvoie false lorsqu'il n'y a plus de lignes dans l'objet ResultSet , elle peut être utilisée dans une boucle while pour parcourir le jeu de résultats.

Vous devrez positionner le ResultSet sur une ligne, par ex. en appelant appel next(), avant d'essayer de lire des données. Si l'appel à next() renvoie false, le jeu de résultats est vide.

+0

Cette réponse # 4. Quel abiout # 1-3? :) – DVK

+0

@DVK Cela n'a pas de sens d'appeler 'getString' tant que le' ResultSet' est positionné sur une ligne, donc il est logique qu'il soulève une exception (cela s'applique à tout pilote JDBC). Je ne suis pas sûr de ce que vous faites allusion au «drapeau» ouvert du «Resultset». –

+0

Dans le débogueur d'Eclipse, lors de l'examen du jeu de résultats, le membre "open" a une valeur "false". C'est la raison de l'exception "ResultSet closed" (pas la cause sous-jacente, mais la variable immédiate examinée pour générer l'exception dans la source JDBC) – DVK

1

Pour ce genre de problème que vous pourriez utiliser comme ça

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

Prenez soin de formater le message. J'ai formaté une de vos réponses, vérifiez-la et formatez les autres réponses, le cas échéant! –

+0

merci Paresh Mayani je suis nouveau pour ce site. –

+0

Pas de soucis ... Continuez à contribuer! –