W3docs

Java JDBC ResultSet

Parcourez et lisez les lignes d'une requête SQL en Java avec l'interface ResultSet.

Un ResultSet est un curseur sur les lignes renvoyées par une requête. Il ne conserve pas toutes les lignes en mémoire à la fois ; il pointe sur une ligne à la fois et vous le faites avancer. Point crucial : un ResultSet fraîchement créé est positionné avant la première ligne — vous devez appeler next() une fois pour atteindre la première. C'est pourquoi toute boucle de lecture s'écrit while (rs.next()).

Vous obtenez un ResultSet à partir d'une requête exécutée via un Statement ou un PreparedStatement. Cette page explique comment parcourir ce curseur et lire les colonnes en toute sécurité.

La boucle de lecture

String sql = "SELECT id, name, score FROM player ORDER BY score DESC";
try (Statement st = conn.createStatement();
     ResultSet rs = st.executeQuery(sql)) {
  while (rs.next()) {                       // advance; false when no more rows
    int id = rs.getInt("id");               // read by column name...
    String name = rs.getString(2);          // ...or by 1-based index
    int score = rs.getInt("score");
    System.out.println(id + " " + name + " " + score);
  }
}

Lire les colonnes : par nom ou par index

Les deux fonctionnent. Les noms de colonnes sont plus clairs et résistent à une réorganisation du SELECT ; les index de colonnes (à base 1, comme tout dans JDBC) sont légèrement plus rapides. Les noms l'emportent pour la maintenabilité dans presque tout le code. Si vous avez besoin des noms, des types ou du nombre de colonnes à l'exécution — par exemple pour afficher un tableau générique — lisez-les depuis le ResultSetMetaData obtenu avec rs.getMetaData().

Le problème de NULL et wasNull()

Les accesseurs primitifs ne peuvent pas retourner null. getInt retourne 0 pour un NULL SQL, getDouble retourne 0.0, etc. — indiscernables d'un vrai zéro. Lorsqu'une colonne est nullable et que la différence est importante, appelez rs.wasNull() immédiatement après l'accesseur :

int score = rs.getInt("score");
if (rs.wasNull()) { /* it was SQL NULL, not a zero */ }

(Pour les types objet, getObject retourne null directement, ce qui est souvent plus propre.)

Type de curseur et concurrence

Par défaut, un ResultSet est TYPE_FORWARD_ONLY et CONCUR_READ_ONLY — vous avancez et vous lisez uniquement. Demandez TYPE_SCROLL_INSENSITIVE pour appeler previous(), absolute(n), ou first(), ou CONCUR_UPDATABLE pour modifier des lignes sur place. Ces options ont un coût plus élevé, ne les demandez que si nécessaire.

Fermez-le (try-with-resources s'en charge)

Un ResultSet maintient un curseur côté serveur ; le laisser ouvert gaspille des ressources de base de données. Fermer son Statement le ferme également, et try-with-resources gère les deux. Ne retournez jamais un ResultSet ouvert depuis une méthode dont la Connection a déjà été fermée.

Exemple concret : le curseur avant uniquement et wasNull

Ce programme modélise le curseur ResultSet avec une petite liste en mémoire — démarrant avant la première ligne, avançant avec une étape de type next() — et reproduit le comportement de getInt retournant 0 pour NULL avec une vérification wasNull, ainsi que les constantes de curseur.

java— editable, runs on the server

Ce que l'on retient de l'exécution :

  • Le curseur démarre à l'index -1avant la première ligne — et l'étape ++cursor imite rs.next() : avancer d'abord, puis lire. C'est exactement pourquoi une vraie boucle s'écrit while (rs.next()) et ne lit jamais une colonne avant le premier next().
  • Les lignes sont lues une par une, sans être chargées sous forme de liste. Le modèle utilise une liste par simplicité, mais un vrai ResultSet diffuse les lignes depuis le serveur, ce qui lui permet de gérer des ensembles de résultats bien plus grands que la mémoire.
  • Le score null de Linus est affiché comme 0 — le même piège que tend getInt. Sans l'indicateur, il serait impossible de distinguer un score manquant d'un vrai zéro.
  • Le marqueur (wasNull) est le discriminant. En JDBC réel, vous appelez rs.wasNull() juste après l'accesseur, car il rend compte de la colonne lue la plus récemment — lire une autre colonne d'abord change la réponse.
  • Les constantes (TYPE_FORWARD_ONLY, CONCUR_READ_ONLY, FETCH_FORWARD) décrivent le curseur par défaut, le moins coûteux : avancer, lire uniquement. Vous optez délibérément pour des curseurs déroulables ou modifiables, car ils demandent au serveur de faire davantage.

Pratique

Pratique
Une requête lit une colonne 'score' nullable avec rs.getInt('score') et obtient 0. Comment savoir si la valeur était un vrai 0 ou un NULL SQL ?
Une requête lit une colonne 'score' nullable avec rs.getInt('score') et obtient 0. Comment savoir si la valeur était un vrai 0 ou un NULL SQL ?
Was this page helpful?