Aller au menu - Aller au contenu

Icône Allons un peu plus loin

Mise à jour : 17/06/2009
Difficulté : Difficile Difficile Creative Commons BY-NC-SA
3 526 visites depuis 7 jours, dont 173 sur ce chapitre classé 45/786
Nous avons vu comment faire des requêtes SQL, les exécuter et les afficher.
Le moment est venu de creuser un peu la question en allant un peu plus en profondeur...

Nous allons revenir sur les objets Statement et ResultSet en détaillant un peu le tout ! :)
Sommaire du chapitre :
Icône du chapitre
Chapitre précédent Sommaire Chapitre suivant

Statement

Vous avez vu comment obtenir un objet Statement ; par contre, ce que vous ignorez, c'est que je ne vous ai pas tout dit... :-°
Je sais, je suis un vilain cysboy...

Vous savez déjà que, pour récupérer un objet Statement, vous devez le demander gentiment à un objet Connection, ceci en invoquant la méthode createStatement() . Je ne vous apprends rien, là ! Du moins j'espère... :D

Par contre, ce que vous ne savez pas, c'est que vous pouvez spécifier des paramètres pour la création de l'objet Statement...
Ces paramètres permettent différentes actions lors du parcours des résultats via l'objet ResultSet.

Paramètres pour la lecture du jeu d'enregistrements :
  • TYPE_FORWARD_ONLY : le résultat n'est consultable qu'en avant. IMPOSSIBLE de revenir en arrière lors de la lecture ;
  • TYPE_SCROLL_SENSITIVE : le parcours peut se faire en avant ou en arrière, le curseur peut se positionner n'importe où mais si des changements sont faits dans la base pendant la lecture, ces changements ne seront pas visibles ;
  • TYPE_SCROLL_INSENSITIVE : comme le précédent sauf que les changements sont directement visibles lors du parcours des résultats.


Paramètres concernant la possibilité de mise à jour du jeu d'enregistrements :
  • CONCUR_READONLY : les données sont consultables en lecture seule. Pas de mise à jour des valeurs pour mettre la base à jour ;
  • CONCUR_UPDATABLE : les données sont modifiables et, lors d'une modification, la base est mise à jour.


Par défaut, les ResultSet issus d'un Statement sont TYPE_FORWARD_ONLY pour le type de parcours et CONCUR_READONLY pour les actions possibles.


Ces paramètres sont des variables statiques de la classe ResultSet, vous savez donc comment les utiliser... ;)
Voici comment créer un Statement permettant à l'objet résultat de pouvoir être lu d'avant en arrière avec possibilité de modification :

Code : Java
1
Statement state = conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_UPDATABLE);


Facile... ^^
Vous avez appris à créer des Statement avec des paramètres, mais saviez-vous qu'il existe un autre type de Statement ?
Nous verrons comment utiliser les fonctionnalités des ResultSet juste après ce point...

Les requêtes préparées

Il va falloir vous accrocher un tout petit peu...
De tels objets sont créés exactement de la même façon que des Statement classiques, sauf qu'au lieu d'avoir :

Code : Java
1
Statement stm = conn.createStatement();

nous avons :

Code : Java
1
PreparedStatement stm = conn.prepareStatement("SELECT * FROM classe");


Jusqu'ici, rien de particulier ! Cependant, une différence est déjà effective à ce stade : la requête SQL est pré-compilée !
Ceci a pour effet que celle-ci s'exécutera plus vite dans le moteur SQL de la BDD, c'est sûr puisqu'il n'aura pas à la compiler... :-°
En fait, en règle générale, on utilise ce genre d'objet pour des requêtes ayant beaucoup de paramètres ou des requêtes pouvant être exécutées plusieurs fois...

C'est tout ce qu'il y a comme différence ?

Non, bien sûr. Je vous ai dit dans le titre que nous allons préparer des requêtes !.
Il y a une différence de taille entre l'objet PreparedStatement et l'objet Statement : dans le premier, on peut utiliser des paramètres à trous !

Quoi ? o_O

Je me doutais que vous auriez du mal à comprendre... En fait, vous pouvez insérer un caractère spécial dans vos requêtes et remplacer ce caractère grâce à des méthodes de l'objet PreparedStatement en spécifiant sa place et sa valeur (son type étant défini par la méthode utilisée...).

Voici un exemple :

Code : Java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
package com.sdz.prepare;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;

public class Prepare {

	public static void main(String[] args) {
		try {
			Class.forName("org.postgresql.Driver");
			
			String url = "jdbc:postgresql://localhost:5432/Ecole";
			String user = "postgres";
			String passwd = "postgres";
			
			Connection conn = DriverManager.getConnection(url, user, passwd);
			Statement state = conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_UPDATABLE);
			
			//On crée notre requête
			String query = "SELECT prof_nom, prof_prenom FROM professeur";
			//Premier trou pour le nom du professeur
			query += " WHERE prof_nom = ?";
			//Deuxième trou pour un id de professeur
			query += " OR prof_id = ?";
			
			//On crée notre objet avec la requête en paramètre
			PreparedStatement prepare = conn.prepareStatement(query);
			//On remplace le trou numéro 1 => le nom du professeur
			prepare.setString(1, "MAMOUD");
			//On remplace le trou numéro 2 => l'id du professeur
			prepare.setInt(2, 2);
			//On affiche
			System.out.println(prepare.toString());
			//On remodifie le trou numéro 1 
			prepare.setString(1, "TOTO");
			//On ré-affiche
			System.out.println(prepare.toString());
			//On remodifie le trou numéro 2
			prepare.setInt(2, 159753);
			//On ré-affiche
			System.out.println(prepare.toString());
			
                        prepare.close();
                        state.close();

		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}


Ce qui nous donne :

Image utilisateur


Je sais, ça fait toujours ça la première fois... :p
Je ne vous cache pas que ceci peut s'avérer très utile.
En effet ! Mais qu'y a-t-il comme méthode d'affectation de valeur ?

C'est simple : vous vous souvenez de la petite liste de méthodes de l'objet ResultSet pour récupérer des données ? À peu de choses près, la même mais avec setXXX à la place de getXXX. ;)

Tout comme son homologue sans trou, cet objet peut prendre les mêmes types de paramètres pour la lecture et pour la modification des données lues :
Code : Java
1
2
3
4
5
PreparedStatement prepare = conn.prepareStatement(
							query, 
							ResultSet.TYPE_SCROLL_INSENSITIVE, 
							ResultSet.CONCUR_READ_ONLY
						);


Vous avez aussi une méthode retournant un objet ResultSetMetaData mais encore une méthode permettant de nettoyer les changements de valeur des trous : prepare.clearParameters(); .
Si vous ajoutez cette méthode à la fin de ce que je vous ai fait tout à l'heure et que vous affichez à nouveau le contenu de notre objet, vous aurez ceci :

Image utilisateur


Bon, je pense que le moment est venu de voir l'objet ResultSet plus en détail...

ResultSet 2 : le retour

Nous allons voir comment se promener dans nos objets ResultSet, vu que nous avons vu comment permettre cela... ^^

En fait, l'objet de résultat offre beaucoup de méthodes afin de pouvoir se promener dans les résultats.
Cela, si vous avez bien préparé l'objet Statement.

Vous avez la possibilité de :
  • vous positionner avant la première ligne de votre résultat : res.beforeFirst() ;
  • savoir si vous êtes sur l'avant-première ligne : res.isBeforeFirst() ;
  • vous positionner sur la première ligne de votre résultat : res.first() ;
  • savoir si vous êtes sur la première ligne : res.isFirst() ;
  • vous retrouver sur la dernière ligne : res.last() ;
  • savoir si vous êtes sur la dernière ligne : res.isLast() ;
  • vous mettre après la dernière ligne de résultat : res.afterLast() ;
  • savoir si vous êtes sur l'après-dernière ligne : res.isAfterLast() ;
  • aller de la première ligne à la dernière : res.next() ;
  • aller de la dernière ligne à la première : res.previous() ;
  • vous positionner sur une ligne précise de votre résultat : res.absolute(5) ;
  • vous positionner sur une ligne par rapport à votre emplacement actuel : res.relative(-3) .


Je vous ai concocté un morceau de code mettant en oeuvre tout ceci...
Vous devriez retrouver les informations vu que ce code est commenté. ;)

Code : Java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
package com.sdz.resultset;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;

public class Resultset {
	public static void main(String[] args) {
		try {

			//--------------------------------------------------------------------
			//TOUT CECI, VOUS CONNAISSEZ			
			Class.forName("org.postgresql.Driver");
			String url = "jdbc:postgresql://localhost:5432/Ecole";
			String user = "postgres";
			String passwd = "postgres";
			
			Connection conn = DriverManager.getConnection(url, user, passwd);
			Statement state = conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE);
			
			String query = "SELECT prof_nom, prof_prenom FROM professeur";			
			ResultSet res = state.executeQuery(query);
			//--------------------------------------------------------------------
			
			//*
			int i = 1;			
			
			System.out.println("\n\t---------------------------------------");
			System.out.println("\tLECTURE STANDARD.");
			System.out.println("\t---------------------------------------");
			
			while(res.next()){
				System.out.println("\tNom : " + res.getString("prof_nom") + " \t prénom : "+ res.getString("prof_prenom"));
				//On regarde si nous sommes sur la dernière ligne du résultat
				if(res.isLast())
					System.out.println("\t\t* DERNIER RESULTAT !\n");
				i++;
			}
			
			//Une fois terminé, nous contrôlons si nous sommes bien à l'extérieur des lignes de résultat
			if(res.isAfterLast())
				System.out.println("\tNous venons de terminer !\n");
			
			System.out.println("\t---------------------------------------");
			System.out.println("\tLecture en sens contraire.");
			System.out.println("\t---------------------------------------");
			
			//Nous sommes donc à la fin
			//Nous pouvons parcourir le résultat en sens contraire
			while(res.previous()){
				System.out.println("\tNom : " + res.getString("prof_nom") + " \t prénom : "+ res.getString("prof_prenom"));

				//On regarde si on est sur la première ligne du résultat
				if(res.isFirst())
					System.out.println("\t\t* RETOUR AU DEBUT !\n");
			}
			
			//On regarde si on est sur l'avant-première ligne du résultat
			if(res.isBeforeFirst())
				System.out.println("\tNous venons de revenir au début !\n");
			
			
			System.out.println("\t---------------------------------------");
			System.out.println("\tAprès positionnement absolu du curseur à la place N° " + i/2 + ".");
			System.out.println("\t---------------------------------------");
			//On  positionne le curseur sur la ligne i/2, peu importe où on est
			res.absolute(i/2);
			while(res.next())
				System.out.println("\tNom : " + res.getString("prof_nom") + " \t prénom : "+ res.getString("prof_prenom"));
			
			System.out.println("\t---------------------------------------");
			System.out.println("\tAprès positionnement relatif du curseur à la place N° " + (i -(i-2)) + ".");
			System.out.println("\t---------------------------------------");
			//On  met le curseur à la ligne actuelle moins i-2
			//Si nous reculons donc de i-2 lignes
			//Si nous n'avions pas mis de signe moins, nous aurions avancé de i-2 lignes 
			res.relative(-(i-2));
			while(res.next())
				System.out.println("\tNom : " + res.getString("prof_nom") + " \t prénom : "+ res.getString("prof_prenom"));
			
                        res.close();
                        state.close();

		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}


Voyez ce que donne ce code :

Image utilisateur


Je pense que la majorité des choses ont été vues ici...
Par contre, faites bien attention à l'endroit où vous vous situez dans le parcours de la requête !
Il y a des emplacements particuliers. Par exemple, si vous n'êtes pas encore positionnés sur le premier élément et que vous faites un rs.relative(1) , vous vous retrouvez sur le premier élément. De même, un rs.absolute(0); correspond à un rs.beforeFirst() !


Donc, lorsque vous voulez spécifiez la place du curseur sur la première ligne, vous utiliserez absolute(1); et ceci, peu importe où vous vous trouvez !

Par contre, ceci nécessite que le ResultSet soit de type TYPE_SCROLL_SENSITIVE ou TYPE_SCROLL_INSENSITIVE, sinon vous aurez une exception !


Bon, rien de bien méchant ici...
Je vous propose donc de clore ce chapitre par un petit QCM avant de continuer sur notre lancée...

Q.C.M.

Par défaut, quels sont les attributs de lecture et de modification d'un ResultSet ?
Quelle est la méthode d'un objet Connection permettant de créer une requête préparée ?
Quel est le caractère utilisé dans les requêtes préparées pour symboliser un champ à remplir ultérieurement ?
Peut-on spécifier la façon dont on va gérer les résultats d'une requête préparée comme une requête habituelle (façon de parcourir, modifiable...) ?
Quelle ligne va être lue après ces instructions ?
Code : Java
1
2
3
4
5
6
7
8
ResultSet rs = state.executeQuery("SELECT * FROM classe");
rs.absolute(3);
rs.relative(2);
rs.relative(-3);
rs.relative(5);
rs.absolute(4);

//lecture des données

Statistiques de réponses au QCM

J'espère que ce chapitre vous a permis d'y voir plus clair sur le fonctionnement global de tout ce mic-mac...
Chapitre précédent Sommaire Chapitre suivant

Partager

3 commentaires pour "Allons un peu plus loin"
Note moyenne : 3.34 / 4 (178 votes)
Pseudo Commentaire
Hors ligne softdounia # Posté le 11/08/2009 à 15:10:08
A
Avatar

Ville : Alger
Pays : Algérie
Études : ETS Montréal

Je croie moi en plus que les requête préparé aide sur tous à avoir le résulta de la requête à porter de main dans la RAM (imaginer le cas une BD distribuée à plusieurs KM de distance) en aura un grand gin de temps.

Des commentaires sur ma remarque seront les bienvenus

Sinon :magicien: c'est un magicien cysboy :soleil: :zorro: :waw:

Les 40e rugissants et les 50e hurlants. o_O --> Brainstorming --> CamelCase
Image utilisateur

-------------
"je crois qu'on ne peut mieux vivre qu'en chercant à devenir meilleur, ni plus agréablement qu'en ayant pleine conscience de son amélioration"
Socrate >_<
-------------
les meilleurs peintres se sont fait dire qu’ils n’étaient pas bon, aujourd’hui ce sont des Dieux!
-------------
RTFM :-° l'expression anglaise d'argot Internet Read the fucking manual (« Lis le foutu manuel ») ;
-------------
Mon CV en ligne ici
 
Hors ligne tatat # Posté le 18/06/2010 à 11:42:34

Merci pour ce tuto tres utile!
Concernant la classe Prepare d'un des exemple, je ne comprend pas
pourquoi on instancie un objet state qui ne sert pas!
Hors ligne Mini Vado # Posté le 24/02/2012 à 15:28:23
Avatar

Études : Exia.Cesi, Ecole Supérieure d'Informatique du Cesi

Tutoriel qui débrouissaille le fonctionnement du resultset.

Malheureusement pas du tout orienté objet et c'est compliqué à mettre objet vu que les paramètres dépendent directement de la connexion...

Développeur Web (HTML 5 & Css3 | JS | Php 5)
 

Voir tous les commentaires