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 :

  • TYPE_FORWARD_ONLY : c'est le mode par défaut. Le curseur ne peut se déplacer que d'une ligne à la fois, et uniquement vers l'avant. Le mode de sensibilité n'est pas défini, dépend de la base de données que l'on utilise, et de son pilote.

  • TYPE_SCROLL_INSENSITIVE : signifie que tous les mouvements du curseur sont possibles, y compris son positionnement sur une ligne directement. Le mode insensible signifie que des modifications faites à la base ne sont pas transmises au ResultSet, ce qui signifie que la lecture plusieurs fois d'une même ligne renverra toujours le même résultat. En revanche, on ne sait pas si les données sont lues au moment de l'exécution de la commande SQL, ou au moment de la lecture d'une ligne.

  • TYPE_SCROLL_SENSITIVE : de même, tous les mouvements du curseur sont possibles. Les changements faits sur la base de données sont reflétés dans le ResultSet. Donc deux lectures successives d'une même ligne peuvent donner des résultats différents.

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.


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.

  • next() : déplace le curseur sur la ligne suivante. Elle retourne true si le curseur est positionné sur une ligne, false si le curseur a dépassé la fin du tableau. La valeur du retour de cette méthode doit obligatoirement être testée avant la lecture de la ligne courante.

  • previous() : déplace le curseur sur la ligne précédente. Retourne true ou false, suivant que le curseur se positionne sur une ligne, ou en dehors du tableau.

  • first(), last() : positionne le curseur sur la première ligne ou la dernière ligne du tableau. Retourne true ou false suivant que cette ligne existe ou pas.

  • beforeFirst(), afterLast() : positionne le curseur sur l'une des lignes virtuelles se trouvant avant la première ligne, ou après la dernière. Ce mouvement est toujours possible, même sur un tableau vide.

  • relative(int rows) : déplace le curseur du nombre de lignes indiqué. Le déplacement a lieu vers le bas du tableau si ce nombre est positif, vers le haut s'il est négatif. Si rows vaut 0, alors le curseur ne bouge pas. Si le tableau ne comporte pas assez de ligne, vers le haut ou vers le bas, alors le curseur se positionne sur l'une des ligne virtuelle avant la première ligne, ou après la dernière. Cette méthode retourne true si le curseur a été positionné sur une ligne, false dans le cas contraire.

  • absolute(int rows) : positionne le curseur en absolu dans le tableau. La première ligne du tableau est numérotée 1, donc absolute(1) positionne le curseur sur cette première ligne. On peut passer un paramètre négatif à cette méthode. Dans ce cas, les lignes sont numérotées à partir de la dernière, et en commençant par -1. Ainsi absolute(-1) positionne le curseur sur la denière ligne du tableau, absolute(-2) sur l'avant-dernière etc... Si rows est plus grand que le nombre de lignes, alors le curseur se positionne sur la ligne virtuelle qui se trouve après le tableau si rows est positif, et sur la ligne virtuelle avant le tableau s'il est négatif. L'appel à absolute(0) positionne le curseur avant la première ligne du tableau.

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 :

  • Un entier de type int, qui doit correspondre au numéro de la colonne que l'on cherche. Les colonnes sont numérotées à partir de 1.

  • Une chaîne de caractères String, qui doit correspondre au nom de la colonne.

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 :

  • la modification des champs d'une ligne ;

  • l'ajout d'une ligne ;

  • la suppression d'une ligne.

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)

La création d'une ligne dans un ResultSet est une option du standard, qui n'est pas supportée par tous les pilotes.

L'insertion d'une ligne est un processus qui se déroule en trois temps :

  1. Placer le curseur sur une ligne virtuelle particulière par appel à la méthode moveToInsertRow().

  2. Renseigner les valeurs de cette ligne pour toutes les colonnes, par les méthodes update<Type>(Type).

  3. Insérer cette nouvelle ligne dans le tableau par appel à la méthode insertRow().

Une fois notre nouvelle ligne insérée, on peut positionner le curseur dessus en appelant la méthode moveToCurrentRow(). Voyons ceci sur un exemple.


Là encore, c'est le résultat de l'appel aux méthodes DatabaseMetaData.ownInsertsAreVisible(int type) et DatabaseMetaData.otherInsertsAreVisible(int type) qui nous dira si le ResultSet voit les modifications qu'il a lui-même faite sur la base. Là encore il faut tester sur le ResultSet voit ses propres modifications ou celles des autres, par les méthodes

Java Database Connectivity
Retour au blog Java le soir
Cours & Tutoriaux