Types d'objets ResultSet

Pour comprendre ce qui suit, il faut s'imaginer qu'un ResultSet est en fait un tableau dont les colonnes sont celles qui ont été extraites par notre requête SQL, et dont les lignes sont les résultats de cette requête. Examiner les résultats de notre requête consiste donc à examiner le contenu de ce tableau, ligne par ligne. La ligne courante est désignée par un curseur, dont il va être question dans la suite.

Le type d'un ResultSet recouvre deux choses : les mouvements possibles pour le curseur, et la sensibilité du résultat à la base de données sous-jacente. Effectivement, la spécification JDBC ne précise pas si le tableau de résultat doit être lu en une fois, au moment de l'exécution de la requête, ou par paquets, au fur et à mesure que l'on balaye ses lignes. Dans le deuxième cas, la question peut se poser : est-on en train de lire la base telle qu'elle était au moment de la requête, ou bien ce que l'on voit prend-il en compte des modifications qui pourraient avoir eu lieu par ailleurs ?

Il y a trois types de ResultSet, définis par des constantes de cette interface :

Le type de ResultSet que l'on veut peut être imposé lors de la création d'un Statement, en fixant son paramètre resultSetType. On peut tester notre connexion afin de savoir si elle supporte le type de ResultSet que l'on veut, comme dans l'exemple suivant.

Deux valeurs sont disponibles sur resultSetConcurrency :

Là encore, tous les pilotes ne supportent pas nécessairement la mise à jour des données au travers du ResultSet. On peut interroger les méta-données de la base de la même façon que pour le type de la façon suivante.

Lorque la méthode commit() est appelée sur la transaction, manuellement ou automatiquement, il se peut que les ResultSet ouverts sur cette connexion soient également fermés, ce qui n'est pas toujours le résultat désiré. Après tout, si ces ResultSet sont ouverts en lecture seule, rien ne devrait pouvoir s'oppsoser à ce que l'on puisse continuer à lire les données qu'ils contiennent.

Là encore deux valeurs sont possibles pour le paramère resultSetHoldability :

Ici la norme JDBC ne définit pas de comportement par défaut, il faut donc interroger la connexion afin de savoir dans quel mode on se trouve.

On obtient un objet ResultSet le plus souvent en invoquant la méthode executeQuery(String) d'un objet Statement.

Un ResultSet peut être vu comme un tableau de résultats, dont chaque colonne est un champ, et chaque ligne un enregistrement. La lecture des lignes d'un ResultSet se fait au travers d'un curseur, que l'on peut déplacer ligne par ligne. Lors que l'on obtient un objet ResultSet, par exécution de la méthode executeQuery(String), ce curseur est positionné sur une ligne virtuelle, qui se trouve avant la première ligne du tableau.

La première catégorie de méthodes proposées par l'interface ResultSet va donc nous permettre de déplacer le curseur dans ce tableau.

Voici ces méthodes.

Si notre ResultSet est de type FORWARD_ONLY, alors la seule méthode possible est next().

Une fois le curseur positionné sur une ligne valide, il est possible d'accéder aux valeurs de chaque cellule. Pour cela, ResultSet expose une collection de méthode de type get<Type>(), qui permet de lire la valeur de chaque cellule dans son bon type.

Ces méthodes peuvent prendre deux types de paramètre :

Notons que l'on peut obtenir le numéro d'une colonne par la méthode findColumn(String) de ResultSet.

La mise à jour d'une base de données direcement au travers d'un ResultSet est possible si le resultSetConcurrency est de type CONCUR_UPDATABLE. Dans ce cas, trois types d'opérations sont possibles :

Il est possible de modifier la ligne courante d'un ResultSet en utilisant une des méthodes update<Type>(int, Type), qui prend en paramètre le numéro de la colonne et la nouvelle valeur à attribuer à cette colonne pour cette ligne.

On peut de cette façon modifier autant de colonnes que l'on souhaite sur la ligne courante. La modification sera prise en compte sur l'appel à la méthode updateRow() de ResultSet. On peut également annuler ces par l'appel à la méthode cancelRowUpdates()

Notons qu'un ResultSet ne "voit" pas nécessairement les modifications que l'on fait sur lui. Cela signifie que si l'on lit la valeur d'une cellule que l'on vient de modifier, il n'est pas garanti que l'on lise la valeur modifiée, quand bien même elle a été correctement prise en compte.

On peut interroger la méthode DatabaseMetaData.ownUpdatesAreVisible(int type) afin de savoir si un ResultSet voit les modifications qu'on lui applique, pour un type (java.sql.Types) donné.

De même, un ResultSet peut voir ou non les modifications faites dans d'autres ResultSet (et donc éventuellement dans d'autres transactions), et l'on peut tester ce comportement grâce à la méthode DatabaseMetaData.otherUpdatesAreVisible(int type)

Un ResultSet peut être (et doit être !) explicitement fermé par un appel à sa méthode close(). Une fois fermé, on ne peut plus accéder à son contenu. Notons que la fermeture d'un ResultSet n'entraîne pas la fermeture des objets qui lui sont attachés, notamment les BLOB.