Tutoriel sur les Eléments de programmation en Delphi

PROGRAMMER ADO EN DELPHI
TABLE DES MATIERES
TABLE DES MATIERES .1
INTRODUCTION .4
PREAMBULE .4
ADO: ACTIVEX DATA OBJECT ..4
DEFINITION 4
Consommateur & fournisseur de données ..5
Fournisseur & composant de service 5
Jeu d'enregistrement (Recordset) 5
Propriétés statiques & dynamiques (Properties) .6
MODELE OBJET .6
LA CONNEXION .6
Le fournisseur ..7
La source de données 7
Synchronisation ..7
Erreurs 7
Transactions .8
Mode d'ouverture 8
LE RECORDSET (JEU D'ENREGISTREMENT) 9
Les Curseurs .9
Méta-données 14
Données (Fields) .14
Mécanismes de base ..14
Modification d'un Recordset client ..18
FIREHOSE, UN CURSEUR PARTICULIER 21
CONSEILS POUR CHOISIR SON CURSEUR ..22
SYNCHRONE VS ASYNCHRONE ..22
OPERATION GLOBALE (PAR LOT) 22
Les transactions 22
Les procédures stockées 22
Gérée par le code (traitement par lot) ..23 LE PIEGE "L'EXEMPLE JET" 23
RECORDSET VS SQL 24
RECORDSET PERSISTANT ..24
OPTIMISATION DU CODE 24
L'optimisation dans l'accès aux données ..24
L'OBJET COMMAND ..25
Communication vers le SGBD 25
Communication bidirectionnelle ..25
Création de requêtes et de procédures ..25
Collection Parameters ..25
TADOCONNECTION 27
Propriétés 27
Propriétés dynamiques ..30
Méthodes .32
Evènements .35 TADOCOMMAND .36 Propriétés 36
Méthodes .39
OBJETS INCLUS: TBOOKMARK, TFIELD, TINDEXDEF ..40
TBookmark .40
TField 40
TIndexDef 43
TADOQUERY .44
Propriétés 44
Propriétés dynamiques ..51
Méthodes .54
Evènements .60
TADOTABLE ..63
Propriétés et méthodes spécifiques ..63
TADODATASET 64
TADOSTOREDPROC 64
EXEMPLES DE CODE ..65
NOMBRE D'ENREGISTREMENT ET POSITION ..65
COMPARAISON SQL VS RECORDSET ..66
RECHERCHE SUCCESSIVES OU DIRECTIONNELLES ..68
Les défauts de la méthode Locate .68
Gestion des signets .69
Programmation intrinsèque 72
PROGRAMMATION ASYNCHRONE ..74
Connection et command asynchrone ..75
Extractions bloquantes & non bloquantes 76
Suivre l'extraction 77
Gestion des modifications 77
RECORDSET PERSISTANT 78
SYNCHRONISATION ..79
GENERATION DE COMMANDES 81
Les trois valeurs de TField ..81
Similaire au moteur de curseur .81
Commande paramétrée .85
TRAITEMENT PAR LOT .90
Gestion standard des erreurs .93
Actions correctives ..95
Utiliser une transaction 95
ACCES CONCURRENTIEL 96
Les procédures stockées 97
Le verrouillage .97
Transactions et exclusivité ..98
CONCLUSION SUR ADO .98
ADOX: MICROSOFT ACTIVEX DATA OBJECTS EXTENSIONS ..99
IMPORTER LA BIBLIOTHEQUE ADOX DANS DELPHI. 99
MODELE OBJET 100
RAPPELS ACCESS 100
Sécurité .100
Paramétrage JET ..100
NOTIONS FONDAMENTALES ..101
ADOX & Access 101
Propriétaire .101ParentCatalog 101
L'OBJET CATALOG .101
COLLECTIONS DE L'OBJET CATALOG .102
COLLECTION TABLES 102
COLLECTION PROCEDURES 103
COLLECTION VIEWS .103
COLLECTION GROUPS ..103
COLLECTION USERS ..103
L'OBJET TABLE 103 Collection Properties ..104
Collection Columns .104
Objet Column .105
COLLECTION INDEXES .108
Objet Index ..108
COLLECTION KEYS 109 Quelques notions ..109
Méthode Append 110
Objet Key .110
Exemples ..111
Conclusion sur les tables ..113
L'OBJET PROCEDURE 113
Création d'un objet procédure .113
Modification d'un objet Procedure 114
L'OBJET VIEW 114
CONCLUSION SUR LES OBJETS VIEW & PROCEDURE ..114
GESTION DES UTILISATEURS ..114
CAS PARTICULIER D'ACCESS .115
PROPRIETES ET DROITS 115
Propriétaire .115
Administrateur 115
Utilisateurs et groupes 116
Héritage des objets ..116
OBJET GROUP ..116 SetPermissions 116
GetPermissions ..119
OBJET USER .120
ChangePassword ..120
GetPermissions & SetPermissions 120
Properties .120
EXEMPLE 120
TECHNIQUES DE SECURISATION ..121
Modification 122
Création 122
Une autre solution : le DDL .122
CONCLUSION SUR LA SECURITE 122
CONCLUSION ..122
INTRODUCTION
Dans cet article nous allons regarder comment utiliser les accès à des sources de données en utilisant ADO (ActiveX Data Object) et ADOX (extension pour la structure et la sécurité) avec Delphi 7. Dans un premier temps nous découvrirons les technologies ADO ainsi que les concepts fondamentaux qu'il convient de connaître afin de les utiliser correctement. Dans la suite, nous verrons comment Delphi encapsule ces objets et permet de les mettre en œuvre en utilisant les composants ou le code, enfin nous regarderons quelques exemples afin de découvrir quelques astuces.
Pour ceux qui ont déjà lu d'autres articles, que j'ai écrit sur ADO pour VB, vous allez naviguer en terrain nouveau. En effet, du fait de l'encapsulation Delphi, la manipulation ADO est grandement modifiée, je dirais même améliorée pour de nombreux points. S'il n'est pas possible d'utiliser ADO sans connaître ses principes fondamentaux, il est tout aussi vain d'essayer de le manipuler comme en VB ou en C++.
PREAMBULE
Lorsqu'on est dans l'éditeur Delphi, on est en droit de se demander quel peut bien être l'intérêt de connaître les mécanismes de fonctionnement d'ADO. Il est tout d'abord évident qu'il s'agit de l'attaque d'une base de données autre qu'Interbase. Dans le cas de base de données de type Access ou Sql-Server, la meilleure stratégie consiste à utiliser les composants ADO intégrés dans Delphi depuis la version 6. Ils sont assez semblables aux contrôles standards et on peut penser qu'il n'y a rien de plus à connaître.
Le petit exemple qui va suivre va vous montrer pourquoi un tel apprentissage est indispensable pour peu que l'on veuille faire autre chose que de la simple consultation.
Imaginons une table de base de données contenant des doublons parfaits et n'ayant pas de contrainte d'unicité.
Dans ma feuille, j'ajoute un composant TADOConnection dont je paramètre la propriété ConnectionString afin de me connecter à la source de données, je crée un composant ADODataset (ou ADOTable) qui utilise cette connexion et au travers d'un composant DataSource, j'alimente un composant DBGrid. A l'exécution, ma table apparaît. Si je change une valeur de ma table dans une ligne qui n'est pas un doublon, pas de problème, la modification est répercutée dans la source de données. Par contre si cette ligne est un doublon, j'obtiens une erreur. Si je regarde mon composant ADODataset, il possède une propriété CursorLocation dont la valeur est clUseClient. En modifiant celle-ci à la valeur clUseServer, mes modifications se font sans erreur quelle que soit la ligne modifiée.
Bien sur une telle table est aberrante, mais ADO comporte de nombreux pièges que nous allons découvrir ensemble.
ADO: ActiveX Data Object
Définition
ADO (ActiveX Data Object) est un modèle d'objets définissant une interface de programmation pour OLE DB.
OLE DB est la norme Microsoft? pour l'accès universel aux données. Elle suit le modèle COM (Component Object Model) et englobe la technologie ODBC (Open DataBase Connectivity) conçue pour l'accès aux bases de données relationnelles. OLE DB permet un accès à tout type de source de données (même non relationnelles). OLE DB se compose globalement de fournisseurs de données et de composants de service.
Consommateur & fournisseur de données
Dans la programmation standard des bases de données, il y a toujours une source de données (dans le cas de cet article, une base Access exemple : ).
Pour utiliser cette source de données, il faut utiliser un programme qui sait manipuler ces données, on l'appelle le fournisseur (dans certains cas serveur). Un fournisseur qui expose une interface OLE DB est appelé Fournisseur OLE DB.
Dans le cas qui nous intéresse, votre code, qui demande des données est le consommateur (ou
client).
Attention, dans certains articles portant notamment sur les contrôles dépendants vous pourrez trouver ces termes avec une autre signification. En effet, si vous liez des zones de champ (DBEdit) à un contrôle DataSource, le contrôle DataSource sera (improprement) appelé Fournisseur de données et vos zones de champs seront consommatrices.
Pourquoi est-ce impropre ?
Comme on ne voit pas le code que le contrôle DataSource utilise pour demander des informations, on tend à faire l'amalgame entre les deux. Mais c'est une erreur car le contrôle est le consommateur de données. Nous verrons l'importance de cela avec les curseurs et le paramétrage des contrôles de données.
Fournisseur & composant de service
Un fournisseur de service permet d'ajouter des fonctionnalités au fournisseur de données. Il y en a plusieurs dans ADO tel que "Microsoft Data Shaping Service" qui permet la construction de jeux d'enregistrements hiérarchiques ou "Microsoft OLE DB Persistence Provider" qui permet de stocker les données sous forme de fichiers.
Un composant de service n'a pas d'existence propre. Il est toujours invoqué par un fournisseur (ou plus rarement par d'autres composants) et fournit des fonctionnalités que le fournisseur n'a pas. Dans cet article nous allons beaucoup parler du "Service de curseur pour Microsoft OLE DB" plus couramment appelé moteur de curseur.
Jeu d'enregistrement (Recordset)
Lorsque le fournisseur extrait des données de la source (requête SELECT), il s'agit de données brutes (sans information annexe) n’ayant pas un ordre particulier. Celles-ci ne sont pas très fonctionnelles, et il faut d'autres informations pour pouvoir agir sur la source de données. En fait, un recordset (ou dataset) est un objet contenant des données de la base, agencées de façon lisible, et des méta-données. Ces méta-données regroupent les informations connexes des données telle que le nom d'un champ ou son type et des informations sur la base telle que le nom du schéma.
Cette organisation est produite par un composant logiciel qui est le point central de la programmation ADO, le moteur de curseur. Le résultat ainsi obtenu est appelé curseur de données. Il y a dans ce terme un abus de langage qui explique bien des erreurs de compréhension. Le terme curseur vient de l'anglais "cursor" pour "CURrent Set Of Rows". On tend à confondre sous le même terme le moteur de curseur qui est le composant logiciel qui gère l'objet Recordset, l'objet Recordset (données, méta-données et interface) et enfin le curseur de données qui n'est rien d'autre que l'enregistrement en cours. Cet amalgame vient de l'objet Recordset qui contient à la fois des informations destinées au moteur de curseur, des données et des méthodes qui lui sont propres. Dans la suite de cet article comme dans la majorité de la littérature disponible, vous trouverez sous la dénomination "curseur" le paramétrage du jeu d'enregistrement vis à vis du moteur de curseurs.
Propriétés statiques & dynamiques (Properties)
Dans le modèle ADO et a fortiori dans le modèle ADOX, de nombreux objets possèdent une collection un peu particulière : "Properties". Celle ci concerne des propriétés dites dynamiques par opposition aux propriétés habituelles des objets (statiques). Ces propriétés dépendent du fournisseur de données, elles ne sont en général accessibles qu'après la création de l'objet (et éventuellement l'application d'une méthode refresh sur la collection) voire après l'ouverture de l'objet.
On ne peut pas accéder à une propriété statique par l'intermédiaire de la collection Properties.
Un objet Property dynamique comporte quatre propriétés intégrées qui lui sont propres, à savoir :
• La propriété Name qui est une chaîne identifiant la propriété
• La propriété Type qui est un entier spécifiant le type de donnée de la propriété.
• La propriété Value qui est un variant contenant la valeur de la propriété.
• La propriété Attributes qui est une valeur de type Long indiquant les caractéristiques de propriétés spécifiques au fournisseur.
Modèle objet
Le modèle objet ADO est fondamentalement assez simple tel que nous le voyons dans le schéma ci-dessous. Ce schéma est volontairement incomplet. En effet dans le cadre de ce document nous n'étudierons pas les objets Record et Stream.
La connexion
Contrairement à une idée reçue, avec ADO, il y a toujours une connexion au moins lors de la création des objets de données. Le fournisseur peut être un fournisseur de données ou un fournisseur de service, la source de données peut être distante ou locale, mais la connexion existe toujours. Elle peut être soit explicite c'est à dire déclarée par votre code et/ou vos composants, soit implicite c'est à dire créée par ADO. La connexion est une session unique d'accès à une source de données et dans le cas d'application client/serveur elle représente aussi une connexion au réseau. Globalement une connexion attend toujours au moins deux éléments, un fournisseur et une source de données. Le maintient de cette connexion active est souvent plus une question de ressource que de stratégie, comme nous le verrons dans l'étude du mode déconnecté.
Le fournisseur
ADO en lui-même n'est qu'une interface de haut niveau permettant d'utiliser OLE DB. La connexion doit donc impérativement définir le fournisseur utilisé. Il appartient au développeur de vérifier la présence de ce fournisseur.
N.B : Attention, certains empaquetages récents du MDAC de Microsoft(2.6+) ne contiennent pas certains composants ou fournisseurs comme Microsoft Jet, le fournisseur Microsoft Jet OLE DB, le pilote Desktop Database Drivers ODBC ou le pilote Visual FoxPro ODBC. Pour pouvoir utiliser pleinement les fonctionnalités ADO, il convient d'installer aussi une version plus ancienne (habituellement la version 2.1).
Le fournisseur peut être désigné sous la forme d'un DSN (Data Source Name) ou sous son nom. Je n'utiliserai pas les DSN dans cet article attendu que je déconseille vivement leur utilisation.
Dans la théorie ADO, il est conseillé de déclarer le fournisseur le plus tôt possible. En effet, vous avez remarqué que tous les objets ADO contiennent une collection "Properties". Celle-ci englobe ce que l'on appelle les propriétés dynamiques de l'objet. En fait chaque objet ADO contient quelques propriétés fixes, et de nombreuses propriétés données par le fournisseur, ce qui sous-tend que ces propriétés peuvent ne pas exister selon les fournisseurs. Elles ne sont donc pas accessibles tant que le fournisseur n'est pas défini. Ces propriétés peuvent parfois être indispensables au bon fonctionnement du code.
La source de données
La connexion doit aussi contenir une référence à la source de données. Il est évident que le fournisseur doit pouvoir manipuler cette source. Certaines sources peuvent être accédées par plus d'un fournisseur. Nous verrons avec ADOX qu'il est possible de créer la source de données par le code.
Synchronisation
Une connexion ADO peut être synchrone ou non. Les connexions ADO n'imposent jamais d'être synchrone, mais le développeur peut les forcer à être asynchrones. Je m'explique, une connexion ADO est soit demandée comme asynchrone, soit le fournisseur décide. Il faut donc toujours bien garder à l'esprit qu'une connexion peut ne pas être toujours synchrone, selon la charge du serveur par exemple. De manière générale, il est moins lourd pour le serveur de traiter des connexions asynchrones.
Erreurs
Lorsqu'une erreur ADO se produit, il y a ajout d'un objet "Error" à la collection Errors de la connexion active de l'objet, si tant est que celle-ci existe encore. Une erreur récupérable se produit également. Une erreur classique consiste à croire que la collection garde la trace de toutes les erreurs survenues lors de la session. En fait lorsqu'une erreur se produit, elle crée un ou plusieurs objets "Error" dans la connexion selon que l'erreur entraîne d'autres erreurs en cascade ou non. Si une autre erreur survient par la suite, la collection est vidée et le processus recommence. Certains passage de la programmation ADO comme la mise à jour par lot de jeu d'enregistrement déconnecté demande le traitement de ces erreurs (cf l'exemple Traitement par lot).
Transactions
Il s'agit là d'un vaste sujet, que nous allons survoler seulement pour l'instant. Pour plus de renseignement je vous conseille de lire :
Les transactions par Henri Cesbron Lavau
A quoi servent les transactions ? par frédéric Brouard (SQLPro)
L'utilisation des transactions est fortement recommandée, car elles assurent une cohésion à une suite de traitement nettement plus dur à réaliser avec le code client. D'autant plus qu'on peut imbriquer des transactions. Ceci permet de valider certaines modifications, tout en gardant la possibilité d'annuler l'ensemble à la fin, l'inverse présentant moins d'intérêt.
Dans certains cas, on peut remplacer les transactions par du code client, mais celui-ci doit être géré comme une transaction.
Comme vous l'avez lu dans l'article de Frédéric, le point de difficulté se situe autour de l'isolation. D'ailleurs, la grande difficulté de la programmation des SGBD repose sur la gestion des accès concurrentiels. Il faut toujours suivre les règles suivantes :
v Tout risque de lecture sale est à bannir v La transaction doit être courte v Dans le doute, la transaction doit échouer.
Notez enfin que certains fournisseurs ne gèrent pas tous les degrés d'isolation. Nous verrons dans les exemples, ce qu'il faut faire et ne pas faire
Mode d'ouverture
Lorsqu'on ouvre une connexion ADO, on lui donne un certain nombre de privilèges d'accès à la source.
L'exclusivité définit une connexion qui doit être unique sur la source. Une telle connexion ne peut s'ouvrir s'il y a déjà une connexion sur la source, et aucune autre connexion ne pourra avoir lieu sur une source ayant une telle connexion.
L'exclusivité étant très pénalisante, la connexion possède une propriété Mode qui définit soit ses propres droits soit qui restreint les droits des autres connexions ; étant bien entendu qu'une connexion restrictive peut restreindre les droits d'une connexion déjà ouverte. Il convient donc d'être vigilant lors de la restriction des droits.
Le recordset (jeu d'enregistrement)
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 :
v 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’enregistrements. 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.
v 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’envoi de modifications vers la base de données, les modifications apportées par 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.
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.
Curseur côté serveur (clUseServer)
ADO utilise par défaut des curseurs Serveurs (mais Delphi des curseurs clients). Ceux ci fournissent des recordset qui ont moins de fonctionnalités que leurs homologues clients, mais ayant les avantages suivants :
Diminution du trafic réseau : Les données n'ayant pas à transiter vers le client Economie 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ée.
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ées
La connexion doit constamment restée ouverte
Curseur côté client (clUseClient)
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 important :
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.
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".
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 verrouillage :
Le verrouillage pessimiste (ltPessimistic)
Pose un verrou sur les enregistrements dès que l'utilisateur tente de modifier une valeur (on dit à l'édition). Le verrou dure jusqu'à la validation des modifications. Si un enregistrement est déjà verrouillé, il se produit une erreur récupérable lorsqu'un autre utilisateur tente de le modifier
Le verrouillage optimiste (ltOptimistic)
Ce n'est pas un verrou stricto sensu, mais une comparaison de valeur. 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 d'un enregistrement, le numéro de version change, toutes les modifications d'autres utilisateurs ayant alors un mauvais numéro de version, celles-ci échouent.
ADO supporte aussi un mode appelé optimiste par lot (ltBatchOptimistic), 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 (ltReadOnly) qui interdit la modification des données du recordset
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 :
procedure TForm1.FormActivate(Sender: TObject); begin with ADOConnection1 do begin ConnectionString:='.OLEDB.4.0;Data Source=d:\ ;'; Connected:=true; ConnectionObject.Properties['Jet OLEDB:Page Timeout'].Value:=10000; end; with ADOQuery1 do begin ('SELECT * FROM Authors'); Connection:=ADOConnection1; CursorLocation:= clUseServer; LockType:= ltOptimistic; CursorType:=ctKeyset; Active:=true; :=fields[1].Value; end; end; procedure TForm1.Button1Click(Sender: TObject); begin with ADOQuery1 do begin Edit; fields[1]; Post; end; end; |
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 connexion, à 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.
Type de curseur (CursorType)
Dans cette propriété sont mêlées le défilement et la sensibilité aux modifications des données.
Le défilement est une fonctionnalité du curseur. Elle représente la faculté qu'a le jeu d'enregistrement de refléter la position de l'enregistrement en cours, ainsi que sa faculté à organiser et à se déplacer dans le jeu de données.
La sensibilité aux données représente la faculté qu'a le 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 iPar position, on entend la possibilité de déterminer la position de l'enregistrement en cours
au sein du jeu d'enregistrements.
Il existe quatre types de curseur :
En avant seulement (ctOpenForwardOnly)
Position £ Non
Défilement £ En avant
Sensibilité £ Pas de mise à jour des données modifiées certaines, seuls les enregistrements non accédés seront mis à jour.
Ce type de curseur est le plus rapide. Idéal pour la lecture de données en un seul passage.
Statique (ctStatic)
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 clé (ctKeyset)
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 utiliser avec des tables indexées. Très rapide car il ne charge pas les données
mais juste les clés.
Dynamique (ctDynamic)
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
Le poids lourd des curseurs. N'est pas supporté par tous les fournisseurs.