IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)

Comprendre les Recordset ADO


précédentsommairesuivant

IV. Les Curseurs

Avec ADO, impossible de parler de recordset sans parler du curseur de données qui va créer ce recordset. La quasi-totalité des problèmes rencontrés lors de la programmation ADO sont dus à un mauvais choix ou à une mauvaise utilisation du curseur. Il faut dire à la décharge du développeur que ceux-ci peuvent être assez difficiles à programmer, d'où l'idée de cet article. Schématiquement le curseur doit gérer deux sortes de fonctionnalités :

Celles propres à la manipulation des données à l'aide du recordset

  • Comme je l'ai déjà dit, les données renvoyées n'ont pas de notion d'enregistrement en cours ou de navigation entre enregistrements. Pour concevoir facilement l'objet Recordset il faut le voir comme une collection d'enregistrement. C'est le curseur qui permet de définir l'enregistrement en cours et s'il est possible d'aller ou pas vers n'importe quel autre enregistrement à partir de l'enregistrement en cours. C'est aussi lui qui permet de faire un filtrage, une recherche ou un tri sur les enregistrements du recordset.

La communication avec la source de données sous-jacente.

  • Cette communication comprend plusieurs aspects tels que l'actualisation des données du recordset, l'envoie de modifications vers la base de données, les modifications apportées pas les autres utilisateurs, la gestion concurrentielle, etc.

Malheureusement il n'y a pas une propriété pour chacune de ces fonctionnalités. En fait, ces fonctionnalités se définissent par la combinaison de deux propriétés, le verrouillage (LockType) et le type du curseur (CursorType).

Mais avant de définir ces propriétés, il convient de choisir le positionnement du curseur et c'est là que commencent les problèmes.

IV-A. Positionnement (CursorLocation)

Voilà le concept qui fait le plus souvent défaut. C'est pourtant un point fondamental. La position du curseur définit si les données du recordset sont situées dans le Process du client, c'est-à-dire votre application ou dans celui du serveur, c'est-à-dire celui du fournisseur. De plus le moteur du curseur et la bibliothèque de curseur seront donnés par le fournisseur si la position est côté serveur, alors qu'ADO invoquera le moteur de curseur client (composant de service) si la position est du côté client. La différence est primordiale, car si vous n'avez pas à vous préoccuper du fonctionnement interne du fournisseur, il est utile de connaître le fonctionnement du moteur de curseur client. Nous verrons dans la discussion sur les curseurs clients tout ce que cela peut avoir d'important. La bibliothèque de curseur définit aussi quelles fonctionnalités sont accessibles comme nous le verrons un peu plus loin.

Certaines propriétés / méthodes ne fonctionnent que si le curseur est du côté client et d'autres que s'il est du côté serveur. Nous verrons plus loin quand choisir l'un ou l'autre et pourquoi.

IV-A-1. Curseur côté serveur (adUseServer)

ADO utilise par défaut des curseurs Serveurs. Ceux-ci fournissent en des recordset ayant moins de fonctionnalités que leurs homologues clients, mais ont les avantages suivants :

  • Diminution du trafic réseau : Les données n'ayant pas à transiter vers le client
  • Économie des ressources du client puisque celui-ci ne stocke pas les données.
  • Efficacité de mise à jour. Comme c'est le fournisseur qui gère le recordset, celui-ci affiche les modifications en « temps réel » pour peu qu'un curseur ayant cette faculté ait été demandé.
  • Facilité de programmation. Le fournisseur prenant en charge le curseur gère aussi directement les actions sur la source de données.

Ils sont très performants pour les petits recordset et les mises à jour positionnées.

Il faut par contre garder à l'esprit :

  • Que chaque client connecté va consommer des ressources côté serveur
  • Que le nombre de connexions, côté serveur, n'est pas illimité
  • La connexion doit constamment rester ouverte

IV-A-2. Curseur côté client (adUseClient)

Les curseurs côté client présentent les avantages suivants :

  • Nombreuses fonctionnalités ADO (tri, filtrage, recherche…)
  • Une fois que les données sont dans le cache de votre application, celles-ci sont aisément et rapidement manipulables
  • Possibilité de travailler hors connexion, voire de stockage sur le disque

Par contre, ils présentent deux inconvénients importants :

  • La surcharge de la connexion est facile à atteindre, en cas de modifications fréquentes des données
  • Ils sont délicats à programmer comme vous allez avoir le plaisir de vous en rendre compte.

IV-B. Fonctionnalités (bibliothèque de curseur)

Une fois définie la position, il vous faut choisir les fonctionnalités de votre recordset, et donc utiliser un des curseurs de la bibliothèque. Un curseur se définit en valorisant les propriétés LockType et CursorType de l'objet Recordset. Ces propriétés doivent être valorisées avant l'ouverture du recordset. Le curseur va donc définir :

Le défilement

L'accès concurrentiel

L'actualisation des données du recordset

Autant dire maintenant que le choix d'un mauvais curseur peut donner des comportements aberrants à votre application.

Piège n° 1 Lorsque vous demandez un curseur qui n'existe pas dans la bibliothèque, le moteur ou le fournisseur vous en attribuera un autre n'ayant pas les mêmes fonctionnalités sans toutefois déclencher d'erreur. Le choix de l'autre reste à mes yeux un mystère, puisque la documentation dit « le curseur le plus proche ».

IV-B-1. Verrouillage (LockType)

Le verrouillage (accès concurrentiel) est un élément indispensable des SGBD MultiUtilisateurs. Le verrouillage consiste à interdire la possibilité a deux utilisateurs (ou plus) de modifier le même enregistrement en même temps. Il y a globalement deux modes de verrouillages :

Quel verrou choisir ?

Les considérations de ce paragraphe seront sur les curseurs côté serveurs. La principale différence entre ces deux modes de verrouillage vient du comportement optimiste. En effet, un verrouillage pessimiste ne se préoccupe pas de la version de l'enregistrement. Un enregistrement est ou n'est pas verrouillé. S'il ne l'est pas, rien n'empêche plusieurs utilisateurs de modifier successivement le même enregistrement. Dans le cadre d'un verrouillage optimiste, il n'est théoriquement pas possible de modifier un enregistrement modifié par ailleurs. Cela est partiellement faux, car sur un curseur qui reflète les modifications (KeySet par exemple), le numéro de version sera remis à jour à chaque rafraîchissement du Recordset. Le verrouillage optimiste ne sera donc que temporaire sur un curseur « dynamique ». Ceci implique les observations suivantes :

Le verrouillage pessimiste interdit les modifications simultanées sur un enregistrement, mais pas les modifications successives.

Le verrouillage optimiste peut n'être que temporaire et risque de mal jouer son rôle.

Regardons le code ci-dessous :

 
Sélectionnez
Private cnn1 As ADODB.Connection, MonRs As ADODB.Recordset

Private Sub Command1_Click()

MonRs.Fields("Author").Value = Text1.Text
MonRs.Update

End Sub

Private Sub Form_Load()

Set cnn1 = New ADODB.Connection
cnn1.Open "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=d:\biblio.mdb ;User Id=Admin; Password="
cnn1.Properties("Jet OLEDB:Page Timeout") = 10000
Set MonRs = New ADODB.Recordset
MonRs.Open "SELECT * FROM Authors", cnn1, adOpenKeyset, adLockOptimistic
Text1.Text = MonRs.Fields("Author").Value

End Sub

J'ai modifié la valeur de la propriété dynamique « Jet OLEDB:Page Timeout » afin qu'il y ait 10 secondes entre chaque rafraîchissement du recordset. Si j'exécute deux instances de ce programme simultanément, je n'aurais jamais d'erreur si j'attends au moins 10 secondes entre chaque modification, quelle que soit l'instance. Sinon, une erreur de verrouillage optimiste peut se produire.

Comme souvent avec ADO, ces considérations peuvent forcer le choix du curseur. Par exemple, si vous voulez obtenir un verrouillage optimiste permanent avec MS Jet, vous devez utiliser un curseur statique. Comme Jet ne vous fournira pas un curseur statique acceptant les modifications côté serveur, vous devrez utiliser un curseur côté client

Le verrouillage pessimiste est configurable. Par défaut, si on tente d'accéder à un enregistrement verrouillé, il y aura déclenchement d'une erreur et la tentative d'accès échouera. Cependant on peut paramétrer la connection, à l'aide des propriétés dynamiques « Jet OLEDB:Lock Delay » et « Jet OLEDB:Lock Retry » afin que la pose du verrou soit mise dans une boucle d'attente. Une telle boucle si elle empêche le déclenchement d'une erreur peut facilement encombrer le serveur.

Le verrouillage pessimiste (adLockPessimistic

Ce mode de verrouillage prend effet au moment de l'édition de l'enregistrement. Le fournisseur pose un verrou sur l'enregistrement dès que l'utilisateur tente d'en modifier une valeur (on dit à l'édition). Le verrou dure jusqu'à la validation des modifications. Une erreur récupérable se produit lorsqu'un utilisateur tente de modifier un enregistrement verrouillé.

Le verrouillage optimiste (adLockOptimistic)

Ce mode de verrouillage prend effet lors de l'enregistrement des modifications.Ce n'est pas un verrou stricto sensu, mais une comparaison de valeurs.

Selon les SGBD et aussi selon la structure de la table, la vérification se fait sur un numéro de version, un champ date de modification (TimeStamp) ou la valeur des champs.

Prenons un exemple avec les numéros de version. Chaque recordset prend le numéro de version de l'enregistrement. Lors de la validation des modifications, le numéro de version change.Toutes les modifications d'autres utilisateurs ayant alors un mauvais numéro de version, celles-ci échouent.

D'autres modes de verrouillage

ADO supporte aussi un mode appelé optimiste par lot (adLockBatchOptimistic), qui marche comme expliqué ci-dessus à quelques variations près que nous verrons lors du traitement par lot.

La propriété LockType accepte aussi un mode ReadOnly (adLockReadOnly) qui interdit la modification des données du recordset.

IV-B-2. Type de curseur (CursorType)

Dans cette propriété sont mêlés le défilement et la sensibilité aux modifications des données.

Le défilement est une fonctionnalité du curseur qui représente la faculté du jeu d'enregistrements à :

- refléter la position (1) de l'enregistrement en cours

- organiser le jeu de données

- se déplacer dans ce jeu.

La sensibilité représente la faculté du recordset de refléter les modifications, ajouts, suppressions effectués sur les données.

Je vais vous présenter ces curseurs en précisant à chaque fois leurs fonctionnalités

Il existe quatre types de curseurs :

En avant seulement (adOpenForwardOnly)

Position -> Non

Défilement -> En avant

Sensibilité -> Pas de mise à jour des données modifiées

Ce type de curseur est le plus rapide. Idéal pour la lecture de données en un seul passage.

Statique (adOpenStatic)

Position -> Oui

Défilement -> Bidirectionnel

Sensibilité -> Pas de mise à jour des données modifiées

Copie de données. Les curseurs côté client sont toujours statiques.

Jeu de clefs (adOpenKeyset)

Position -> Oui

Défilement -> Bidirectionnel

Sensibilité -> Reflète les modifications de données, mais ne permet pas de voir les enregistrements ajoutés par d'autres utilisateurs

Demande d'être utilisé avec des tables indexées. Très rapide, car il ne charge pas les données, mais juste les clefs.

Dynamique (adOpenDynamic)

Position -> Oui

Défilement -> Bidirectionnel

Sensibilité -> Reflète toutes les modifications de données ainsi que les enregistrements ajoutés ou supprimés par d'autres utilisateurs

Ce poids lourd des curseurs, uniquement serveur, n'est pas supporté par tous les fournisseurs.

IV-C. Héritage

La création d'un objet Recordset sous-entend toujours la création d'un objet Connection et d'un objet Command. Soit ces objets sont créés de façon explicite soit ils sont créés implicitement par ADO lors de l'ouverture du recordset.

La création implicite pose deux problèmes :

  • Ces objets cessent d'exister lors de la fermeture du Recordset et doivent être récréés à chaque exécution et on multiplie ainsi le nombre de connexions à la base.
  • On oublie de paramétrer correctement ces objets, voire on oublie qui plus est qu'on les a créés, mais le recordset héritant de certaines de leurs propriétés, on n'obtient pas ce que l'on désire.

Ces deux objets se retrouvent dans les propriétés ActiveConnection et ActiveCommand de l'objet Recordset. Cet héritage est important comme nous allons le voir dans la suite de cet article.

Un recordset dont la propriété ActiveConnection vaut Nothing et utilisant un curseur client est un recordset déconnecté.

Un recordset dont la propriété ActiveCommand vaut Nothing est dit « Volatil ».

IV-D. Discussion autour des curseurs

Choisir le curseur dont on a besoin n'est pas très compliqué, dès lors que l'on a bien conscience de ce que l'on va obtenir en gérant le paramétrage. Pour savoir cela, il faut comprendre l'ensemble des mécanismes en jeu. Nous allons aborder quelques points techniques un peu ardus avant d'énoncer des règles générales qui devraient vous aider dans votre choix.

Le fait de choisir la position de son curseur entraîne de nombreuses conséquences, qu'on évalue souvent mal et qui pourtant vont énormément jouer sur le comportement du recordset. Les concepts que nous allons aborder dans cette discussion sont assez divers, aussi accrochez-vous.

IV-D-1. Métadonnées

Le recordset a donc besoin pour fonctionner de données autres que celles renvoyées par la requête, a fortiori si vous allez modifier la base par l'intermédiaire de ce recordset. Ces métadonnées se divisent en deux blocs, celles qui sont stockées dans les propriétés statiques et dynamiques des objets Fields, et celles qui sont stockées dans la collection des propriétés dynamiques de l'objet Recordset. Celles stockées dans les propriétés statiques des objets Fields sont les mêmes, quelle que soit la position du curseur.

Par contre, les propriétés dynamiques de l'objet recordset, ainsi que la collection Properties de chaque objet Field, sont très différentes selon la position du curseur.

Si on compare un recordset obtenu avec un curseur côté client de son homologue côté serveur, on constate qu'il possède beaucoup plus de propriétés dynamiques, alors que le second est le seul à posséder la série des propriétés « Jet OLE DB ».

Ceci vient de la différence fondamentale de comportement du curseur. Lorsqu'on invoque un curseur côté serveur, le fournisseur OLE DB prend en charge la gestion de ce curseur. Il valorise quelques propriétés dynamiques qui lui sont propres, mais le recordset n'a pas besoin de stocker des informations de schéma puisque c'est le fournisseur qui va agir sur la base de données.

IV-D-2. Données (Fields)

Pour stocker les données de la requête, les recordset possèdent une collection Fields. Chaque objet Field représente un champ invoqué par la requête SELECT ayant créé le recordset. Un objet Field comprend :

La valeur de la donnée de l'enregistrement en cours

En fait, la valeur apparaît trois fois. La propriété Value contient la valeur existante dans votre Recordset, la propriété UnderlyingValue contient la valeur existante dans la source de données lors de la dernière synchronisation enfin la propriété OriginalValue contient la valeur de la base lors de la dernière mise à jour. Nous allons voir plus loin tout ce que cela permet et induit.

Les métadonnées du champ (nom, type, précision)

Elles sont disponibles avant l'ouverture du recordset si la connexion est déjà définie dans l'objet Recordset et si la propriété source contient une chaîne SQL valide, sinon on ne peut y accéder qu'après l'ouverture du recordset.


précédentsommairesuivant
Par position, on entend la possibilité de déterminer la position de l'enregistrement en cours au sein du recordset.

Copyright © 2003 bidou. Aucune reproduction, même partielle, ne peut être faite de ce site ni de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.