I. Préambule▲
Ami lecteur , ce qui suit peut être sauter sans nuire à la lecture du tutoriel.
J'ai rédigé cette partie uniquement dans le but de te donner une idée d'ensemble .
J'ai découvert les composants ZEOSDBO , il y a environ cinq ans , suite à l'achat d'un D2010 pro (au lieu d'une version entreprise) et depuis je les utilise régulièrement pour tous mes nouveaux programmes professionnels en lieu et place des composants d'accès aux bases de données fournis .
Que sont les composants ZEOSDBO (aussi connus comme la ZEOSLIB) ?
Comme déjà indiqué dans le résumé de l'article , c'est un ensemble de composants open source , gratuits, permettant d'accéder à plusieurs types de bases de données, dont les principales sont :
- MySQL
- PostgresSQL
- Interbase/Firebird
- Oracle
- SQLite
- MS SQL
- Sybase
- Oracle
Cette liste peut être sujet à changement selon les versions , vous vous référerez au fichier readme de votre version .
La plus ancienne version que j'ai pu télécharger (5.5 commencée en 2001 pour D3-D7, C++ Builder 5-4 et Kylix) proposait une palette de composants spécifiques pour chaque SGBD . Depuis , les auteurs ont préféré faire une couche commune d'accès , ce qui implique moins de composants et surtout une simplification importante . Chaque version majeure voit aussi sont lot de changements sur les IDE supportés et les versions des Bases de Données .
A l'heure où j'écris , la dernière version stable (7.1.3) supporte les IDEs
- Delphi 7,9 et 2005-2010 ainsi que les versions XE à XE7
- Lazarus (FreePascal)
- C++ Builder 12
La version 7.2 est encore en test, j'en parlerais d'avantage lors du prochain chapitre .
II. Téléchargements et Installations▲
Vous avez certainement remarqué que j'utilise le pluriel . En effet plusieurs options sont à envisager : Soit vous optez pour le téléchargement du zip de la version stable sur le site du projet [http://sourceforge.net/projects/zeoslib] soit vous préférez une version SVN et donc plus récente svn://svn.code.sf.net/p/zeoslib/code-0/trunk voire même une des branches
II-A. Installation de la version stable▲
Téléchargez le fichier zip et extraire les fichiers où vous le souhaitez , et ouvrez ensuite votre IDE et recherchez le package que vous souhaitez installer (répertoire packages)
Prenez le temps de :
- Ajouter le chemin de la construction du package (<répertoire d'installation>\packages\<version>\build) aux chemins de des bibliothèques de Delphi.
- Modifier les variables d'environnement de votre poste pour y ajouter ce même chemin
- Jeter un œil sur le fichier zeos.inc dans le répertoire des sources et de le modifier en fonction de vos besoins (inutile d'utiliser tous les protocoles si vous n'en utilisez qu'un par exemple) .
Cela vous prendra peu de temps mais pourrait vous en faire gagner par la suite .
Pour l'exemple , j'ai installé , sur un poste windows 8 64 bits , Delphi dans le répertoire C:\BORLAND et les composants ZEOS dans un sous répertoire de ce dernier C:\BORLAND\ZEOSDBO (ceci pour éviter tout ennui avec l'U.A.C. des windows vista et +).
Ouvrez votre IDE et ouvrez le package correspondant : ici, C:\BORLAND\ZEOSDBO\packages\delphi7\zeosdbo.bpg)
le projet contient 6 unités dont une seule est installable : ZcomponentDesign70.bpl
Compilez dans l'ordre
- ZCore.bpl
- ZParseSql.bpl
- ZPlain.bpl
- ZDbc.bpl
- ZComponent.bpl
ou utilisez l'option du menu 'Projet\Compiler tous les projets'
À ce stade et selon les versions de Delphi vous aurez peut être obtenu un nombre impressionnants d'erreurs d'attention (warnings) . La plupart seront dues au passage à l'Unicode
Puis, si les compilations se sont déroulées sans erreurs graves , installez ZcomponentDesign.bpl . Un message de confirmation d'enregistrement des composants plus tard , vous aurez sur votre palette de composants , la bibliothèque Zeos
II-B. Les versions S.V.N.▲
Je ne me pencherais sur l'utilisation des programmes de subversions trop nombreux (à toutes fins utiles je vous renvois ce WikiWikipedia ainsi qu'à la FAQ et au Forums dédiés à ce sujet ) . Pour ma part j'utilise Tortoise SVN .
Dans le cas d'utilisation d'un dépôt SVN , pas besoin de décompresser d'archive , sinon , suivez les mêmes étapes de compilation .
Dans ce cas , me demanderez vous , qu'est-ce qui change ?
En fait , surtout la possibilité d'avoir une version à jour et de pouvoir tester les versions bêta , les auteurs font régulièrement des corrections . Mais aussi la possibilité d'utiliser/garder plusieurs versions . Bien sur cela amène aussi son lot de tracas :
- Pas de documentation construite
- Les chemins de construction et de recherche à ajouter à chaque démarrage de nouveau projet , sinon gares aux erreurs de ce style
- Une prolifération de DCUs inutiles (sauf si vous avez affiné les options de votre projet)
- Plus d'erreurs d'avertissements (en particulier avec les versions Unicode de Delphi)
Pour pallier , aux erreurs , vous les options de projet et d'environnement comme suit
III. Introduction ▲
Pressé d'en découdre ? Maintenant que les composants sont installés, avant même de rentrer dans la description de chacun des composants , passons à une première petite démonstration .
|
|
A mon avis , cet exemple , souvent proposé est exactement ce qu'il ne faut pas faire pour des programmes d'exploitation . Outre le fait qu'aucun des composant n'est nommé , l'utilisation d'une Table est à proscrire dès que l'on sort du cadre local . Ne prenez pas de mauvaises habitudes . |
Pour ceci nous allons faire notre premier programme avec les composants ZEOS . Cet exemple part du requis que :
- Le moteur SGBD est installé (et démarré) sur le poste de développement (Firebird 2.5 dans notre cas)
- La base de données est déjà crée , et se trouve également sur le poste (local), la structure de la base de données se retrouvera dans l'Annexe A
Ouvrez un nouveau projet Delphi
Sur la forme ajoutez :
- 1 TZConnection
- 1 TZTable
- 1 TdataSource
- 1 TDBGrid
- 1 TDBNavigator
Modifions ensuite les propriétés de certains composants
ZConnection1.Protocol=firebird_2_5
ZConnection1.HostName=localhost
ZConnection1.User=SYSDBA
ZConnection1.Password=masterkey
ZConnection1.Database=F:\DataBase\JudoClub.fdb
ZTable1.Connection=ZConnection1
ZTable1.TableName=CLASSEMENT_COMPETITION
Dataset1.Dataset=ZTable1
DBGrid1.Datasource=Dataset1
DBNavigator1.DataSource=Dataset1Une fois cela effectué , revenons sur ZTable1 et mettons sa propriété Active=True .
Si tous les prérequis ont été respectés , et toujours en mode design , nous obtenons ceci :
IV. La palette des composants ZEOSDBO▲
La genèse de ce tutoriel part des constatations suivantes :
- il existe de nombreux tutoriels et exemples de programmes d'accès aux base de données via le BDE
- de nombreux messages sur notre forumForums developpez.net font état de l'utilisation de ce dernier pour attaquer des tables paradox
- les composants de la bibliothèque ZEOS se sont voulus 'BDE similaire' mais en enlevant cette couche de connexion
- BDE a été déclaré obsolète il y a environ 10 ans par Borland (à l'époque) et son installation n'est (enfin) plus fournie depuis XE7
J'ai tout d'abord voulu , en faisant le recensement des composants de la bibliothèque ZEOS , faire un tableau comparatif avec BDE . Le lecteur pourra trouver un comparatif BDE/ADO/DBExpress/Interbase Express, s'il a la chance d'avoir le manuel papier de Delphi7 ,page 15-25 , sinon vous pouvez toujours aller voir Appendice B : Composants d'accès au données Delphi 7 .
Comparatif ZEOSDBO et la palette BDE
|
Composant |
Objectif |
Équivalent BDE |
|---|---|---|
|
ZConnection |
Connexion à la base de données |
TDataBase |
|
ZQuery |
Accès aux données en utilisant une instruction SQL |
TQuery* |
|
ZReadOnlyQuery |
Accès aux données en utilisant une instruction SQL |
TQuery |
|
ZTable |
Accès a une table de la base de données |
TTable |
|
ZSQLUpdate |
Ensemble d'instructions SQL permettant la modification d'un ensemble non dynamique |
TUpdateSQL |
|
ZStoredProc |
Accès aux procédures stockées |
TStoredProc |
|
ZSQLProcessor |
Permet l'exécution de scripts SQL (plusieurs instructions) |
- |
|
ZSQLMetadata |
Accès aux métadonnées (structure) de la base de données |
- |
|
ZSequence |
Accès aux compteurs (autoincréments) |
- |
|
ZSQLMonitor |
Monitoring de la base de données |
- |
|
ZIBEventAlerter |
Certaines base de données (Interbase/Firebird) |
- |
|
ZConnectionGroup |
Objets permettant de fournir un accès aux bases de données dans un environnement multithread |
- |
|
Pas d'équivalent, quoique les groupes de connexion pourrait s'en approcher ? |
TSession |
|
|
Pas d'équivalent (ce qui est fort dommage) |
TBatchMove |
Les prochains chapitres seront consacrés à chacun de ces composants .
Quelques conventions avant de commencer :
- En gras les propriétés, méthodes importantes
- En gras italique celles qui bien que moins importantes peuvent être utiles
- En rouge , importantes, à voir dans le « coin du mécano »
- le reste , ne sera pas forcément mis en avant
V. TZConnection : La connexion à la Base de Données▲
C'est le premier composant que vous mettrez sur votre fiche ou dans le DataModule . En tout cas , tous les autres composants ZEOS ou presque devront y être rattachés .
Ce composant créé de manière implicite une transaction . Cette combinaison est logique parce que tous les accès à une base de données Firebird (mais aussi à d'autres bases de données) se font toujours dans une transaction en cours d'exécution. Une telle opération est lancée par la Bibliothèque ZEOS chaque fois qu'une connexion (méthode Connect de TZConnection) à une base de données est ouverte.
Un recensement c'est bien , du concret c'est mieux.
Sautez directement à la partie En pratique
V-A. Les propriétés de TZConnection ▲
|
Auto commit |
Mode de la transaction |
|
AutoEncodeStrings |
Auto encodage des chaînes de caractères |
|
Catalog |
Catalogue des données pour PostgresSQL |
|
ClientCodePage |
Encodage coté client ASCII,NONE,WIN1252 |
|
Connected |
État de la connexion |
|
ControlsCodePage |
Concerne de fait votre version d'IDE |
|
DataBase |
Nom de la base de données , pour Firebird on pourra utiliser son nom d'alias |
|
DesignConnection |
Utilisable uniquement au moment du design (donc dans l'IDE) |
|
HostName |
Nom de l'hôte (poste/serveur hébergeant la base de données) |
|
LibraryLocation |
Emplacement explicite de la bibliothèque de connexion à la base de données (driver) |
|
LoginPrompt |
Ouverture du dialogue d'identification |
|
Name |
Nom du composant |
|
Password |
Mot de passe de connexion à la base de données |
|
Port |
Port d'écoute . Si rien n'est indiqué des valeurs seront prise par défaut (3050 pour Firebird, |
|
Properties |
Propriétés supplémentaires , distinct selon chaque type de BDD |
|
Protocol |
Protocole de connexion |
|
ReadOnly |
Mode en lecture seule |
|
SQLHourGlass |
Curseur d'attente |
|
Tag |
- |
|
TransactIsolationLevel |
Niveau d'isolement de la transaction |
|
UseMetadata |
Utilisation des métadonnées |
|
User |
Utilisateur de la BDD |
|
Version |
Version de la ZEOSLIB (7.1.3-stable) lecture seule |
V-B. Les évènements▲
|
AfterConnect |
Après la connexion |
|
AfterDisconnect |
Après la déconnexion |
|
AfterReconnect * |
Après l'instruction Reconnect |
|
BeforeConnect |
Avant la connexion |
|
BeforeDisconnect |
Avant la déconnexion |
|
BeforeReconnect * |
Avant la reconnection |
|
OnCommit |
Lors de l'utilisation de l'instruction Commit |
|
OnLogin |
Au moment de l'identification |
|
OnRollBack |
Lors de l'instruction RollBack |
|
OnStartTransaction |
Au commencement d'une transaction explicite |
V-C. Les méthodes▲
V-C-1. Les procédures et propriétés en lecture seule▲
|
Connect |
Connexion |
|
Disconnect |
Déconnexion |
|
Reconnect |
Reconnexion |
|
GetProtocolNames |
Liste des protocoles utilisables |
|
RegisterDataSet(DataSet: TDataset); |
|
|
UnregisterDataSet(DataSet: TDataset); |
|
|
GetCatalogNames(List: TStrings); |
Liste des catalogues ( PostgresSQL) |
|
GetSchemaNames(List: Tstrings); |
Liste des schémas (PostgresSQL) |
|
InTransaction: Boolean; |
Transaction en cours |
|
StartTransaction |
Démarrer une transaction |
|
Commit |
Appliquer la transaction |
|
Rollback; |
Revenir à l'état avant la transaction |
|
PrepareTransaction(<nom transaction>) |
Préparer une transaction identifiée |
|
CommitPrepared(<nom transaction>) |
Appliquer la transaction identifiée |
|
RollbackPrepared(<nom transaction>) |
Revenir sur la transaction identifiée |
|
DbcDriver: IZDriver |
|
|
DbcConnection: IZConnection |
|
|
ClientVersion |
Numéro de version du client (PostgresSQL) |
|
ServerVersion |
Numéro de version du client (PostgresSQL) |
|
ClientVersionStr |
Version du client |
|
ServerVersionStr |
Version du serveur |
|
GetTableNames |
Liste des tables |
|
GetStoredProcNames |
Liste des procédures stockées |
|
GetTriggerNames |
Liste des triggers |
|
ShowSQLHourGlass |
Afficher le sablier, en relation avec la propriété SQLHourGlass |
|
HideSQLHourGlass |
Cacher le sablier, en relation avec la propriété SQLHourGlass |
|
RegisterSequence |
|
|
UnregisterSequence |
V-C-2. les fonctions ▲
|
Ping: Boolean |
|
PingServer: Boolean |
Ces deux instructions me semblent identiques , la première implique en plus qu'il faut que la ZConnection soit établie . De fait , pour Interbase/Firebird PingServer ne renvoie une valeur positive que si la connexion est active donc …
Attention il ne s'agit pas d'un ping « classique » sur une adresse IP (comme pingserver le suggèrerai) mais d'une opération permettant de savoir le moteur de base de données est actif . J'utilise le mot moteur a escient pour bien insister sur le fait qu'il s'agit du SGBD et non du poste hébergeant la base de données. On a souvent tendance à parler du poste en tant que serveur d'où les confusions possibles. Il est bien sur évident que sur un réseau local , si vous perdez la connexion IP vous perdrez également la connexion avec le SGBD, mais il se peut également que le SGBD soit arrêté (shutdown) ou tout simplement pas démarré !
|
ExecuteDirect(SQL:string):boolean |
|
ExecuteDirect(SQL:string; var RowsAffected:integer) |
Les deux méthodes permettent d'exécuter directement une instruction SQL , la version surchargée permet , en plus , d'obtenir le nombre de lignes affectées .
|
GetBinaryEscapeStringFromString(const BinaryString: AnsiString): String |
|
GetBinaryEscapeStringFromStream(const Stream: TStream): String |
|
GetBinaryEscapeStringFromFile(const FileName: String): String; |
|
GetAnsiEscapeString(const Ansi: AnsiString): String; |
permettent d'obtenir la séquence d'échappement ?
GetURL: String;
GetNamePath : String;
V-D. En pratique ▲
Pour comprendre , créons un programme (téléchargeable ici) dont voici l'image écran . Bien sur , toute la connexion peut être figée (propriétés remplies) au moment du design comme lors de l'Introduction . Cet exemple permet de comprendre les mécanismes de TZConnection et comment fournir les propriétés lors de l'exécution . J'ai essayé de le faire le plus général possible , afin de permettre de tester d'autres SGBD que Firebird , d'autres exemples sont disponibles dans le répertoire « examples » de l'installation du package.
Le premier onglet nous permet de maîtriser la partie connexion au sens strict du terme, le mémo nous sert de traceur à l'exécution .
Pour obtenir les protocoles disponibles (qui je le rappelle peuvent changer en fonction de la version des composants mais aussi en fonction de vos propres modifications dans le fichier zeos.inc) j'ai utilisé la fonction GetProtocolNames
procedure TForm1.FormCreate(Sender: TObject);
begin
// récupération des protocles disponibles
Zconnection1.GetProtocolNames(Protocoles.Items);
// positionnement sur le protocole
Protocoles.ItemIndex:=Protocoles.Items.IndexOf('firebird-2.5');
// Effacement de toutes traces
memo1.lines.clear;
end;Les zones de saisies permettront de renseigner les différentes propriétés pour établir la connexion .
Tout d'abord , l'hôte (hostname) , c'est le nom ou l'adresse (TCP/IP ou Internet) du poste qui héberge la base de données . C'est un parti pris , je suis résolument pour une explication sur une utilisation des bases de données en réseau ! Vous pourriez me rétorquer que vous ne voulez qu'une application sur votre poste (versions embarquées), qu'a cela ne tienne , qui peut le plus peut le moins , il suffira d'indiquer localhost ou l'adresse 127.0.0.1 (exemples 1 et 2) . Pour ce qui est d'un réseau local on utilisera au choix l'adresse IP fixe ou le nom du poste (exemples 3 et 4) . Mais vous remarquerez également que j'ai indiqué, en exemple, une adresse de site Internet ! Hé oui , c'est possible , sous certaines conditions toutefois (que l'hébergement le permette, que le port soit bien redirigé sur le routeur etc ….) .
Ce qui nous amène tout naturellement à la zone suivante : le port . Généralement chaque SGBD a son port par défaut (3050 pour Firebird) , si vous n'indiquez rien ce sera celui-ci qui sera utilisé . Cependant certaines SGBD permettent de changer et donc dans ce cas vous devrez l'indiquer explicitement .
Je ne reviendrai pas sur le protocole , sauf peut être pour vous faire noter le nombre et indiquer que le « d » suivant le nom de la SGBD est pour indiquer le mode embarqué du serveur . Par exemple firebird-2.5 correspond à la version client serveur , firebirdd-2.5 à sa version embarqué .
Passons au nom de la base de données , attention il s'agit du nom sur le poste hébergeant la base . Pour Firebird , on indiquera le chemin complet ainsi que le nom de la base de données
par exemple : F:\DataBase\TEST.FDB , mais , subtilité de Firebird, on peut aussi indiquer un nom d'alias si celui-ci a été renseigné dans aliases.conf .
Nom d'utilisateur et mot de passe se passe de commentaires , passons directement au vif du sujet : la connexion
procedure TForm1.ConnectBtnClick(Sender: TObject);
begin
Memo1.Lines.Add('Clic sur bouton Connect');
try
Zconnection1.Connect;
memo1.lines.add('La connexion est établie') ;
except
on E:Exception do
memo1.lines.add('Erreur Connexion :'+E.message);
end;
end;
procedure TForm1.ZConnection1BeforeConnect(Sender: TObject);
begin
// renseignement de l'hote
Zconnection1.HostName:=Hote.Text;
// renseignement du port
ZConnection1.Port:=StrToIntDef(port.text,0);
// Choix du protocole
Zconnection1.Protocol:=Protocole.Items[Protocole.ItemIndex];
// nom de la base de données
ZConnection1.Database:=Nombase.Text;
// informations de sécurité
ZConnection1.User:=Utilisateur.Text;
Zconnection1.Password:=mdp.Text;
// méthode de login
ZConnection1.LoginPrompt:=DialogPrompt.ItemIndex>0;
case DialogPrompt.ItemIndex of
1 : ZConnection1.OnLogin:=nil
else ZConnection1.Onlogin:=ZConnection1Login;
end;
Memo1.Lines.Add('Événement BeforeConnect')
end;
procedure TForm1.ZConnection1AfterConnect(Sender: TObject);
begin
Memo1.Lines.Add('Événement AfterConnect');
// Modification état des boutons
Connect.Enabled:=False;
Disconnect.Enabled:=True;
Reconnect.Enabled:=True;
end;J'ai fait le choix d'utiliser les événements pour pouvoir tracer plus aisément ce qui se passe. Si tout se passe correctement on obtient ceci :
|
Clic sur bouton Connect |
Nous pouvons maintenant tester la déconnexion
procedure TForm1.ZConnection1BeforeDisconnect(Sender: TObject);
begin
Memo1.Lines.Add('Événement BeforeDisconnect');
end;
procedure TForm1.DisconnectBtnClick(Sender: TObject);
begin
Memo1.Lines.Add('Clic sur bouton Disconnect');
ZConnection1.Disconnect;
end;
procedure TForm1.ZConnection1AfterDisconnect(Sender: TObject);
begin
Memo1.Lines.Add('Événement AfterDisconnect');
// Modification état des boutons
Connect.Enabled:=True;
Disconnect.Enabled:=False;
Reconnect.Enabled:=False;
end;On aura les lignes suivantes :
|
Clic sur bouton Disconnect |
Puis une reconnexion
procedure TForm1.ZConnection1BeforeReconnect(Sender: TObject);
begin
Memo1.Lines.Add('Événement BeforeReconnect');
end;
procedure TForm1.ReconnectBtnClick(Sender: TObject);
begin
Memo1.Lines.Add('Clic sur bouton ReConnect');
ZConnection1.Reconnect;
end;
procedure TForm1.ZConnection1AfterReconnect(Sender: TObject);
begin
Memo1.Lines.Add('Événement AfterReconnect');
end;Une reconnexion ne pourra fonctionner que si la connexion est établie (propriété connected=true)
Résultat :
|
Clic sur bouton Connect |
Revenons maintenant sur le Login , que j'avais laissé de côté . Trois options sont possibles , renseigner directement les informations , utiliser le dialogue standard d'identification ou utiliser l'événement OnLogin . Nous avons déjà vu le premier , le second se passe de commentaire, reste l'événement .
procedure TForm1.ZConnection1Login(Sender: TObject; var Username,
Password: String);
begin
UserName:=Utilisateur.text;
PassWord:=mdp.text;
Memo1.Lines.add('Utilisation de OnLogin pour passer le couple Utilisateur/Mot de passe');
end;Que se passe t-il dans ce dernier cas , lorsque l'on se connecte :
|
Clic sur bouton Connect |
Bien sûr, on ne va pas pour chaque programme demander à l'utilisateur d'indiquer ces renseignements , on préférera utiliser un fichier ini ou tout autre équivalence pour stocker les informations de connexion et rendre ceci transparent . J'aborde ce point plus loin , dans le coin du mécano ,chapitre : La création / les modifications à l'exécution
La deuxième partie du programme (deuxième volet) est une tentative pour obtenir/voir ce que font les différentes fonctions ou propriétés en lecture seule proposées . Malheureusement toutes ne fonctionnent pas encore avec Firebird (l'équipe Zeos cherche toujours des volontaires pour améliorer leur code , d'expérience , entre la soumission de la correction, ou de l'ajout, d'une fonctionnalité et la mise à disposition dans la version SVN le délai est court).
V-E. TZConnection , Le coin du mécano▲
Pourquoi un coin du mécano ? Parce que cette partie s'adresse plus a des programmeurs confirmés voire expert . Aller sur toutes les possibilités et subtilités demande souvent à plonger dans le code source (cela tombe bien il est fourni) .
V-E-1. La création / les modifications à l'exécution▲
Dans ce paragraphe , je ne parle pas de la création de la base de données , mais bien d'une création du composant à l'exécution (run-time) ou de modification de propriétés , par exemple la saisie ou l'utilisation d'un fichier ini pour changer les informations de connexion . Nous avons déjà vu de manière détaillée , les informations nécessaires à la connexion dans la section En pratique . Faisons maintenant au plus simple, en passant par l'intermédiaire d'un fichier .ini les données nécessaires .
[Connexion] Hote=localhost Port= Protocole=firebird-2.5 Utilisateur=SYSDBA mdp=masterkey base=c:\test.fdb
Cette méthode est contestable . Donner la possibilité de voir , en clair , le couple utilisateur/mot de passe est plus que déraisonnable ! Ce type de fichier ini n'est là qu'à titre de démonstration
Pour cette démonstration, j'ai placé la connexion dans un module de données afin de pouvoir la partager . Le DataModule ne contient qu'un seul composant : un TZConnection et j'utilise l'événement OnCreate du DataModule pour l'initialiser .
unit Datamodule;
interface
uses
SysUtils, Classes, ZAbstractConnection, ZConnection, inifiles;
type
TDataModule1 = class(TDataModule)
ZConnection1: TZConnection;
procedure DataModuleCreate(Sender: TObject);
private
{ Déclarations privées }
public
{ Déclarations publiques }
end;
var
DataModule1: TDataModule1;
implementation
{$R *.dfm}
procedure TDataModule1.DataModuleCreate(Sender: TObject);
var F : TIniFile;
begin
F:=TInifile.Create(ChangeFileExt(paramstr(0),'.INI'));
try
ZConnection1.Host:=F.ReadString('Connexion','Hote','localhost');
ZConnection1.Port:=F.ReadInteger('Connexion','Port',0);
ZConnection1.Protocol:=F.ReadString('Connexion','protocole','firebird-2.5');
ZConnection1.Database:=F.ReadString('Connexion','base','c:\test.fdb');
ZConnection1.User:=F.ReadString('Connexion','Utilisateur','SYSDBA');
ZConnection1.Password:=F.ReadString('Connexion','mdp','masterkey');
finally
F.Free;
end;
end;
end.Que manque t-il pour créer la connexion à l'exécution ? Pas grand-chose en fait , on devra juste s'assurer de quelques points : le mode autocommit à true et du niveau d'isolation de la transaction (propriété TransactIsolationLevel mise à tiNone par défaut) . Ce dernier point nécessite l'inclusion dans les uses de ZDBcintfs
ZC:=TZConnection.Create(nil);
try
ZC.Autocommit:=True;
ZC.TransactIsolationLevel:=tiRepeatableRead;
… //suite des propriétés comme au-dessusCe dernier point me permet d'introduire le chapitre suivant
V-E-2. Les Transactions▲
Un peu de théorie n'a jamais fait de mal et certains concepts sur les transactions doivent être connus afin de comprendre comment TZConnection fonctionne de l'intérieur et comment travailler avec. En général, vous n'avez accès à la base de données qu'à l'intérieur d'une transaction valide (« ouverte ») . Une transaction doit remplir 4 caractéristiques connues sous le nom de A.C.I.D.
Atomicité : La propriété d'atomicité assure qu'une transaction se fait au complet ou pas du tout . Si une partie d'une transaction ne peut être faite, toute trace de la transaction doit être effacée et les données remises dans l'état où elles étaient avant cette transaction. Le principe du « tout ou rien »
Cohérence : La propriété de cohérence assure que chaque transaction amènera le système d'un état valide à un autre état valide.
Isolation : La propriété d'isolation assure que l'exécution simultanée de transactions produit le même état que celui qui serait obtenu par l'exécution en série des transactions.
Durabilité : La propriété de durabilité assure que lorsqu'une transaction a été confirmée, elle demeure enregistrée.
Concrètement, une transaction encapsule un ensemble (consécutif) d'accès à la base . Par accès à la base on entend aussi bien une lecture (SELECT) qu'une écriture (INSERT, UPDATE, DELETE) ou un changement à la structure de la base (CREATE,ALTER). Une transaction est terminée par un COMMIT ou un ROLLBACK . Un COMMIT confirme tous les changements dans la base de données depuis le début de la transaction. Un ROLLBACK remettra la base de données à son état d'avant la transaction.
En résumé , des transactions s'exécutant sur un serveur doivent être isolées les unes des autres, de façon a être indépendantes.
En utilisant divers niveaux d'isolation (TIL pour Transaction Isolation Level) un développeur peut protéger les données d'un ensemble de données, résultat d'une requête, de l'accès par d'autres transactions. Ce comportement, le plus standard, est connu sous le nom de SERIALISABLE (l'état d'isolation le plus proche avec Firebird est connu sous le nom de SNAPSHOT) .
Comme je l'ai écrit lors de la présentation du composant TZConnection , dès la création de la connexion une transaction est incluse . Du point de vue des auteurs c'est tout à fait logique car tout accès à une base de données doit se faire via une transaction . Dès que la connexion à la base de données est ouverte (méthode Connect) la transaction est démarrée. Chaque accès à la base est fait à l'intérieur de cette transaction et ce automatiquement . Ce comportement est nommée l'autocommit (propriété autocommit vrai par défaut) , ce qui correspond au comportement standard du composant BDE correspondant. Donc , si autocommit est activé, chaque changement à la base de données sera confirmé (COMMIT) après une exécution réussie.
Cependant , ce comportement peut être désactivé en démarrant de manière explicite une transaction via la méthode StartTransaction . A l'intérieur de cette transaction il est alors possible d'exécuter un ensemble d'instructions SQL qui seront ensuite confirmés par la méthode Commit. Appeler la méthode Rollback annulera toutes les opérations.
StartTransaction met la propriété autocommit à false , Commit ou Rollback la retournera à l'état initial (true) et bien sur arrêtera la transaction.
Retaining
Généralement, après la confirmation d'une transaction via un Commit ou l'annulation via un Rollback la transaction est, normalement, terminée et l'ensemble de données résultats d'une requête ou d'une procédure est jeté. Cela s'appelle le « hard commit » ou « hard rollback ».
Avec la bibliothèque ZEOS ce comportement est légèrement différent , ZEOS garde l'ensemble de données « en vie » . Ceci est fait en terminant la transaction non pas en « hard » mais en « soft » COMMIT ou ROLLBACK , ce mode est le mode « Retaining ».De fait, les commits ou rollbacks d'une transaction Zeos sont faits avec ce mode par défaut, cela s'effectue , de manière transparente, en ré-ouvrant ,tout de suite après la fermeture de la transaction, une nouvelle transaction avec toutes les données et ressources (en particulier les ensemble de données résultants) de la « vieille » transaction.
Cependant le mode retaining peut devenir un problème sur de grosses tables .
Par exemple , ce mode empêche le mécanisme de nettoyage interne de Firebird (garbage collection) ce qui amène a un surcroît d'anciennes données gardées mais inutilement . De part là même cela pénalisera les performances du serveur, qu'un nettoyage (sweep) aurait géré. Mais ce « sweep » n'est fait que en « hard » commit ou rollback , ce que ZEOS ne fait qu'à la fermeture de la connexion . Un contournement possible serait donc de fermer puis rouvrir la connexion régulièrement !
Le comportement « soft commit » peut être être désactivé , via les propriétés de la connexion , voir plus loin Les propriétés supplémentaires
Niveaux d'isolation de la transaction d'une TZConnection (T.I.L.)
Le composant TZConnection fourni des TILs prédéfinis :
tiRepeatableRead
Correspond au « SNAPSHOT » (standard des serveurs Firebird) . C'est une combinaison des paramètres de transaction « concurrency » et « nowait » . Un cliché de la base de données est fait . Les autres utilisateurs sont seulement contraints si deux transactions travaillent en concurrence sur un même enregistrement . Si un conflit est détecté , un message d'erreur sra renvoyé , les changements à l'intérieur des autres transactions dûment notifiés . Ce mode couvre largement le standard SQL nommé « SERIALIZABLE ».
tiReadCommitted
Correspond au mode « READ COMMITTED ». C'est une combinaison des paramètres « read committed » , « rec_version » et « nowait » . Ce niveau prend en compte tout changement validé par d'autres transactions (COMMIT). Le paramètre « rec_version » indique l'on doit prendre compte les valeurs les plus récentes validées par d'autres utilisateurs , « no_wait » qu'il n'y a pas de délai avant de libérer un enregistrement verrouillé.
Ce mode sollicitera d'avantage le serveur car il aura besoin de rafraîchir très souvent les données.
tiSerializable
Correspond au mode « SNAPSHOT TABLE STABILITY » , utilisé pour obtenir un accès exclusif à l'ensemble de données. Obtenu par le biais du paramètre de transaction « consistency » il pare à l'accès aux données de tout autre transaction. Seule la transaction qui a écrit les données peut y accéder, ce qui empêche également tout autre utilisateur d'accéder aux données écrites.
Ce niveau , très restrictif, doit être utilisé avec précaution.
tiNone
Aucun niveau d'isolation pour la transaction
tiReadUncommitted
bien qu'existant , ce dernier n'est pas reconnu par Firebird, l'utiliser lèvera uneerreur et la transaction ne sera pas isolée (comme avec tiNone).
Comme il est recommandé d'utiliser des niveaux d'isolation pour les transactions, tiRepeatableRead sera le choix le plus recommandé .
En second choix , dépendant de l'application , on utilisera tiReadCommitted.
Il est enfin possible de faire ses propres niveaux ou en modifier un existant. On peut le faire en utilisant les paramètres de la TZConnection . Pour ce faire , il vous faudra consulter les documents de référence des API de votre SGBD . Ci-dessous , un exemple de personnalisation à partir de tiNone pour Interbase/Firebird
ZConnection1.TransactIsolationLevel := tiNone;
ZConnection1.Properties.Add('isc_tpb_concurrency');
ZConnection1.Properties.Add('isc_tpb_wait');
ZConnection1.Connect;La théorie c'est bien , mais souvent indigeste , passons donc a un peu de pratique
V-E-3. L'encodage▲
V-E-4. Les propriétés supplémentaires▲
Je vous engage fortement à lire le fichier (pdf ou html) fourni dans le sous répertoire doc de l'installation de la ZEOSLIB. En effet chaque SGBD a ses propres paramétrages.
Le remplissage de ces dernières (il s'agit d'une TStrings) se fait soit au moment du design soit par programme, on ne mettra qu'une propriété par ligne.
Un seul est commun le paramètre defaults=[yes,no] permettant d'indiquer si les valeurs pas défauts doivent être calculés pour les champs NULL . Cependant ce dernier pourra être invalidé par le paramètre au niveau de TZQuery .
Zconnection1.Properties.Add('defaults=yes') ;
// ou encore
Zconnection1.Properties.Values['defaults'] :='yes' ;Pour Interbase/Firebird on trouvera ces propriétés :
- RoleName=<nom du role> . Permet d'indiquer le role à utiliser pour travailler avec la base de données et ainsi d'avoir les privilèges de ce dernier.
-
hard_commit=[yes,no] - Use hard commits instead of soft commits.
Plus annecdotiques de mon point de vue , mais peut être utiles :
-
codepage=<nom de la langue> - Si cette langue est installée pour le SGBD alors les messages d'erreurs seront revoyés dans cette langue.
-
Dialect=<1,2,3> c'est le paramètre du dialect SQL d'Interbase/Firebird.
- CreateNewDatabase=<commande sql de création de la base de données> - Crée la nouvelle base de données décrite dans le TZConnexion, avant d'ouvrir celle-ci.
ZConnection1.Properties.Add ('CreateNewDatabase=CREATE DATABASE ' +
QuotedStr ('c:\test.fdb') + ' USER ' +
QuotedStr ('sysdba') + ' PASSWORD ' + QuotedStr ('masterkey') +
' PAGE_SIZE 4096 DEFAULT CHARACTER SET ISO8859_1');}
try
Zconnection1.Connect;
Zconnection1.Disconnect ;
Zconnection1.Properties.Clear;
IntroMemo.Lines.Add('Création réussie') ;
Zconnection1.Connect;
IntroMemo.Lines.Add('Connexion à la base créée') ;
except
Intromemo.Lines.Add('la création à échoué') ;
end ;
end;VI. TZQuery : L'instruction SQL par excellence▲
Cette classe permet d'accéder à une base de données en utilisant des instructions SQL . Pratique , ce composant, permet d'accéder à plusieurs tables (via les jointures) et/ou un sous-ensemble de lignes (clause WHERE) et colonnes au lieu de tout renvoyer comme le ferait un TZTable . TZQuery et TZReadOnlyQuery sont à mon avis les deux composants à privilégier pour obtenir un ensemble de résultats .
En fait , TZQuery, TZReadOnlyQuery, TZTable et même TZStoredProc sont tout quatre dérivés des mêmes classes . Je vais donc m'employer à décrire le plus possible TZQuery, pour les autres je me contenterai plus , après recensement , d'en souligner les différences et spécificités.
VI-A. Les propriétés▲
|
Active |
État de l'ensemble de données , ouvert ou fermé respectivement vrai ou faux , faux par défaut |
|
AutoCalcFields |
|
|
CachedUpdates |
Activation de la mise à jour en mémoire cache (faux par défaut) |
|
Connection |
Lien à la TZconnection , obligatoire |
|
DataSource |
Liaison a une source de données |
|
FetchRow |
Nombre de lignes à récupérer en une lecture |
|
Filter |
Filtre de l'ensemble de données |
|
Filtered |
Activation du filtre et/ou de l'événement FilterRecord |
|
IndexFieldNames |
|
|
LinkedFields |
|
|
MasterFields |
|
|
MasterSource |
|
|
Name |
Nom du composant |
|
Options |
Options particulières |
|
DoOemTranslate |
|
|
ParamChar |
Caractère signalant un paramètre ( ':' par défaut) |
|
ParamCheck |
Active le contrôle des paramètres |
|
Params |
Liste des paramètres |
|
Properties |
Propriétés supplémentaires |
|
ReadOnly |
Mise en lecture seule (faux par défaut) |
|
Sequence |
Lien vers un TZSequence |
|
SequenceField |
Champ concerné par la séquence (autoincrémentation) |
|
ShowRecordTypes |
Montrer les enregistrements qui : |
|
UsUnmodified N'ont pas été modifiés |
|
|
SortedFields |
Champs du tri , séparés par des virgules |
|
SortType |
Type de tri |
|
SQL |
Instruction SQL |
|
Tag |
Sans commentaire |
|
UpdateMode |
Type de mode de mise à jour voir TZUpdateSQL |
|
UpdateObject |
Lien vers un TZUpdateSQL |
|
WhereMode |
Type de clause where utilisé pour la mise à jour, voir TZUpdateSQL |
VI-B. Les événements▲
|
AfterApplyUpdates |
Après application des mises à jour en cache |
|
AfterCancel |
Après annulation de l'opération en cours |
|
AfterClose |
Après fermeture de l'ensemble de données |
|
AfterDelete |
Après une suppression réussie |
|
AfterEdit |
Après une modification réussie |
|
AfterInsert |
Après une insertion (ajout) réussie |
|
AfterOpen |
Après l'ouverture de l'ensemble de données |
|
AfterPost |
Après validation d'un ajout ou d'une modification |
|
AfterRefresh |
Après rafraîchissement de l'ensemble de données |
|
AfterScroll |
Après déplacement dans l'ensemble de données |
|
BeforeApplyUpdates |
Avant d'appliquer les mises à jour en cache |
|
BeforeCancel |
Avant d'annuler l'opération en cours |
|
BeforeClose |
Avant de fermer l'ensemble de données |
|
BeforeDelete |
Avant de faire une suppression dans l'ensemble de données |
|
BeforeEdit |
Avant de faire une modification sur la ligne en cours |
|
BeforeInsert |
Avant de faire une insertion ou un ajout dans l'ensemble de données |
|
BeforeOpen |
Avant d'ouvrir l'ensemble de données |
|
BeforePost |
Avant validation d'un ajout ou d'une modification |
|
BeforeRefresh |
Avant de rafraîchir l'ensemble de données |
|
BeforeScroll |
Avant de se déplacer à l'intérieur de l'ensemble de données |
|
OnApplyUpdateError |
En cas d'erreur lors de l'application des mises à jour en mémoire cache |
|
OnCalcFields |
Action pour renseigner la valeur des champs « calculés » |
|
OnDeleteError |
En cas d'erreur lors d'une tentative de suppression |
|
OnEditError |
En cas d'erreur lors d'une tentative de mise à jour |
|
OnFilterRecord |
Action de filtre personnalisé |
|
OnNewRecord |
Lors de la demande de création d'une nouvelle ligne |
|
OnPostError |
En cas d'erreur lors de la validation (méthode Post) |
|
OnUpdateRecord |
En cas de mise à jour d'une ligne |
VI-C. Les méthodes▲
La liste en est trop longue pour toutes les énumérées , je me contenterai de grouper par thèmes les plus intéressantes/utiles
VI-C-1. Exécution du SQL▲
|
Open |
Ouverture de l'ensemble de données |
|
Close |
Fermeture de l'ensemble de données |
D'aucun se pose la question de l'utilité de ces instructions , qui ne font que changer la propriété Active à True ou False respectivement, je dirais que ce n'est qu'une question de style. Dans les deux cas , c'est une action à effectuer sur un ensemble de données (SELECT) , à contrario de l'instruction suivante.
|
ExecSQL |
Exécution de l'instruction SQL |
On l'utilisera chaque fois que l'instruction SQL ne renverra aucun résultat (DELETE,UPDATE).
Notez bien la différence entre Open et ExecSQL.
|
FetchAll |
Récupère la totalité des lignes de l'ensemble de données |
Pour quoi faire ? En fait c'est en relation avec la valeur de FetchRows pour plus d'informations allez dans Le coin du mécano
|
EmptyDataset |
Vide l'ensemble de de données |
|
RowsAffected |
Propriété en lecture seule, donnant le nombre de lignes « touchées » par l'instruction SQL |
VI-C-2. Paramétrage d'une instruction SQL▲
Prepare
Prepared
UnPrepare
ParamByName()
VI-C-3. Navigation dans l'ensemble de données▲
First, FindFirst
Next, FindNext
Prior, FindPrior
Last, FindLast
Locate
Lookup
VI-C-4. Les repères (Bookmarks)▲
VI-C-5. Les modifications de l'ensemble de données▲
Insert
InsertRecord
Append
AppendRecord
Edit
Post
Cancel
RevertRecord
RefreshCurrentRow
Les relations maître détail
VI-D. La pratique ▲
VI-E. Le coin du mécano▲
VI-E-1. Les propriétés supplémentaires : Properties▲
Encore une fois , je vous invite à vous référer à la documentation du package (<répertoire de ZEOSDBO>\doc\html\parameters.html ou …\doc\pdf\parameters.pdf)
Deux de ces propriétés sont applicables quelque soit le SGBD :
defaults=[yes,no] - Calcule ou non les valeurs par défaut indiquées pour les champs NULL. Nous avons déjà vu ce dernier pour la TZConnection , la propriété indiquée dans le TZQuery sursoit alors celle qui aurait pu être indiquée pour le TZConnection mais ce , uniquement pour ce composant.
ValidateUpdateCount=[true,false] - Vérifie que la Clause UpdateSQL générée par le composant ZUpdateSQL (ou plus exactement son wizard) affecte une seule ligne. Je vous convie à lire le chapitre TZUpdateSQL, si ce n'est déjà fait .
Deux autres sont spécifiques à Interbase/Firebird :
cursor=<nom du curseur> - Le nom indiqué sera envoyé au serveur de base de données afin de l'utiliser. Pratique pour le monitoring Firebird par exemple (tables MON$xxxxxx)
cashedblob=[yes,no] -Indique si les données contenues dans les blobs seront récupérées immédiatement [yes] ou par l'intermédiaire des API d'Interbase/Firebird [no] .
Cette dernière disparaît au profit de CachedLobs dans la version 7.2 , mais est également plus facile d'accès (toujours dans la version 7.2) grâce à l'ajout de l'option doCachedLobs (propriétés Options)
VI-E-2. FetchRows▲
Attention , FetchRows ne veut pas dire lecture d'un nombre déterminé d'enregistrements , ce que l'on obtient avec une requête Firebird ainsi
SELECT FIRST 10 FROM UNETABLEMais plutôt d'un nombre d'enregistrements par paquet envoyé sur le réseau . Si cette propriété est intéressante pour diminuer le trafic (seuls les enregistrements nécessaires transitent) elle peut avoir des effets de bords inattendus : la valeur de recordcount sera « inexacte » , le curseur vertical d'une DBGrid est fonction du nombre de lignes lues et non de la totalité des lignes de la table.
Pour illustrer ce point, rien ne vaut un petit exemple . Pour cela nous allons interroger une table contenant 19 lignes pour l'afficher dans une grille de 5 lignes . Un SpinEdit , permet de régler la valeur FetchRow avant ouverture de la table , le mémo et quelques gestions d'évènements (AfterOpen et AfterScroll) permettront de tracer le nombre d'enregistrements dans le DataSet , un navigateur complète le tout .
procedure TZQForm.FetchRownBtnClick(Sender: TObject);
begin
if ZQCeintures.Active then QCeintureMemo.Lines.Add('Fermeture de la table');
ZQCeintures.Active:=False;
ZQCeintures.FetchRow:=nbRows.Value;
QCeintureMemo.Lines.Add(Format('Ouverture de la table , FetchRow=%d',[nbRows.Value]));
ZQCeintures.Active:=True;
end;
procedure TZQForm.FetchAllBtnClick(Sender: TObject);
begin
ZQCeintures.FetchAll;
QCeintureMemo.Lines.Add(Format('FetchAll RecordCount = %d',[ZQCeintures.RecordCount]));
end;
procedure TZQForm.ZQCeinturesAfterScroll(DataSet: TDataSet);
begin
QCeintureMemo.Lines.Add(Format('Event AfterScroll RecordCount = %d',[ZQCeintures.RecordCount]))
end;
procedure TZQForm.ZQCeinturesAfterOpen(DataSet: TDataSet);
begin
QCeintureMemo.Lines.Add(Format('Event AfterOpen RecordCount = %d',[ZQCeintures.RecordCount]))
end;Étudions le résultat de l'exécution si FetchRow à la valeur 2 (donc inférieure au nombre de lignes espérée pour la grille)
Malgré (et heureusement) notre demande 5 lignes ont été retournées , le déplacement à l'intérieur de la grille (via le navigateur) , nous montre le comportement suivant
|
Event AfterScroll RecordCount = 5 |
Nous remarquons que , tant qu'il n'y a pas besoin de lecture (dans notre cas jusqu'à 5) , rien n'est fait , puis au fur et à mesure du déplacement le nombre d'enregistrements dans l'ensemble de données augmente . Bref , tout serait parfait sans ce petit « défaut » de la DBGrid (regardez la taille du curseur de la barre verticale de cette dernière) .
Que se passerait-il dans le cas où la valeur de Fetchrow dépassait le nombre de lignes nécessaires ? Comme vous le devinez certainement , c'est le nombre d'enregistrements demandés qui serais renvoyé .
Si FetchRow=0 (valeur par défaut) c'est la table entière qui est envoyée .
FetchAll permet également d'obtenir tout l'ensemble restant à lire
Pour faire bonne mesure , rajoutons une opération de recherche
|
delphi |
0 |
1 |
|||
|
procedure TZQForm.LocateLigne9Click(Sender: TObject); |
|||||
Pas de surprise , l'enregistrement sera bien trouvé , le nombre d'enregistrements lu ,par contre, dépendra de deux facteurs : la position du curseur (tiens , le revoilà) dans la base de données et, bien sur la valeur de Fetchrow.
Si pour un nombre d'enregistrements peu important la propriété FetchRow à peu d'importance , pour une table de taille importante je vous laisse imaginer les avantages sur le trafic réseau.
VI-E-3. Utilisation de la mémoire cache▲
|
ApplyUpdates |
|
|
CommitUpdates |
|
|
CancelUpdates |
|
|
UpdatesPending |
VII. TZReadOnlyQuery : Un ensemble de données en lecture seule▲
Les propriétés
Les méthodes
La pratique
Le coin du mécano
Pour les tatillons sur l'utilisation de la mémoire , la version 7.2 amène une nouvelle propriété isUnidirectional , permettant de mettre l'ensemble de données uni-directionnel et ainsi de le rendre moins gourmand .
VIII. TZTable▲
Les propriétés
Les méthodes
La pratique
Le coin du mécano
IX. TZUpdateSQL▲
Élément incontournable pour une TZQuery modifiable , TZUpdateSQL encapsule un ensemble d'instructions SQL permettant d'actualiser l'ensemble de données . Une requête multitables est par définition selon ,la norme SQL-92, en lecture seule , TZUpdateSQL permet de contourner ce que certains peuvent considérer comme une limitation.
IX-A. Les propriétés▲
|
DeleteSQL |
Instruction(s) pour l'effacement de ligne |
|
InsertSQL |
Instruction(s) d'ajout |
|
ModifySQL |
Instruction(s) de modification(s) |
|
RefreshSQL |
Instruction pour le rafraîchissement de l'ensemble de données |
|
MutiStatements |
Indique si les ordres SQL peuvent contenir plusieurs instruction (vrai par défaut) |
|
Name |
Nom du composant |
|
Tag |
Pas de commentaire |
|
ParamsCheck |
|
|
Params |
|
|
UseSequenceFieldForRefreshSQL |
IX-B. Les événements▲
Les noms de ces événement sont explicites , je n'entrerai pas trop dans les détails
|
AfterDeleteSQL |
Événements après exécution du SQL concerné |
|
BeforeDeleteSQL |
Événements avant exécution du SQL concerné |
Chaque SQL peut comporter plusieurs instructions (si la propriété MutiStatements est à True). Les événements suivants ajoute la possibilité de distingué les différentes instructions contenues , via la valeur de StatementIndex .
|
AfterDeleteSQLStatement |
Événements après exécution du SQL concerné |
|
BeforeDeleteSQLStatement |
Événements avant exécution du SQL concerné |
IX-C. Les méthodes▲
SQL
ExecuteAction
IX-D. La pratique▲
IX-E. Le coin du mécano▲
IX-F. Le coin du curieux▲
X. TZStoredProc▲
Les propriétés
Les méthodes
Les événements
La pratique
Le coin du mécano
XI. TZSQLProcessor▲
Nous avons vu que TZQuery , ou TZReadonlyQuery permettait d'exécuter une instruction SQL ne retournant aucun ensemble de données grâce à la fonction ExecSQL . TZSQLProcessor va plus loin , il permet d'exécuter un ensemble d'instructions SQL , autrement nommé : un script.
L'intérêt ? Outre lors de la création de la base de données pour créer ensuite sa structure, on pourra utiliser ce composant pour l'ajout ou la mise à jour de données en masse.
Une limite toutefois , les blobs binaires seront difficiles (voire impossible selon la taille, les contraintes du SGBD etc ...) à traiter .
XI-A. Les propriétés▲
|
CleanupStatement |
|
|
Connection |
|
|
Delimiter |
|
|
DelimiterType |
|
|
dtDefault |
|
|
Name |
|
|
ParamChar |
|
|
ParamCheck |
|
|
Params |
|
|
Script |
|
|
Tag |
|
XI-B. Les événements▲
|
AfterExecute |
|
|
BeforeExecute |
|
|
OnError |
XI-C. Les méthodes▲
|
LoadFromStream |
Chargement du script par l'intermédiaire d'un flux ou directement à partir d'un fichier contenant des instructions SQL |
|
LoadFromFile |
|
|
Execute |
Exécution du script |
|
Parse |
Interprétation/Analyse du script |
|
Clear |
Effacer le script |
|
ParamByName |
Accès à un paramètre |
|
StatementCount |
Nombre d'instructions contenues dans le script |
|
Statements[index] |
Accès a une instruction particulière par son index |
XI-D. La pratique ▲
XI-E. Le coin du mécano ▲
XII. TZSQLMetadata▲
Les propriétés
Les méthodes
La pratique
Le coin du mécano
XIII. TZSequence▲
Les propriétés
Les méthodes
La pratique
Le coin du mécano
XIV. TZIBEventAlerter▲
Tous les SGBDs ne proposent pas de créer des événements spécifiques comme c'est le cas d'Interbase et Firebird . Tout d'abord qu'est-ce que c'est ? à quoi cela sert ?
Les événements sont des messages simples de notification envoyés de manière asynchrone à partir du serveur aux clients. Chaque fois que l'événement se produit dans la base de données, le serveur avertit tous les clients (connectés) qui ont déclaré leur intérêt pour cet événement. Les événements sont de simples notifications, sans données supplémentaires .
Pour le besoin de l'explication sur l'intérêt , étudions une utilisation commerciale simple :
- une application client-serveur à deux niveaux (2 tiers)
- un poste client crée de nouveaux enregistrements de type "bon d'achats"
- les autres postes clients ont besoin d'être averti des nouveaux dossiers "bon d'achats"
Dans ce scénario, répéter les lectures (par l'intermédiaire d'un timer dans un thread par exemple) de la table «ordre d'achat» provoquerait un trafic réseau à partir de tous les postes de travail clients (même si il n'y a pas de nouvelles commandes) vers tous , et donc trop de postes clients provoquerait une surcharge du réseau et / ou des ressources du serveur. Du fait que l'application soit à deux niveaux, il n'y a pas de couche de service ou de serveur d'application entre la base de données et le logiciel client qui pourrait gérer ces notifications. Pour ce faire nous devons côté serveur envoyer des notifications, afin de ne provoquer un trafic réseau que lorsque de nouvelles données sont envoyées.
Avec les événements Firebird, le code de notification fera partie d'un objet de métadonnées de base de données (un déclencheur ou une procédure stockée) . Par exemple :
CREATE TRIGGER ORDREACHAT_INSERT FOR ORDRES_ACHAT
AFTER INSERT
AS
BEGIN
POST_EVENT 'AJOUT_ORDRE_ACHAT';
ENDTous les postes clients intéressés par cet événement s'inscriront sur cet événement par son nom via une API Interbase/Firebird.
TZIBEventAlerter nous faciliteras cette tâche .
Je dois avouer que dans le cadre de mon application exemple , utilisation de la base de données JudoClub j'ai eu du mal à trouver un cas acceptable. Je me suis rabattu sur un événement sur la création/modification de membre.
Voir le trigger « NOUVEAU_MEMBRE »
La solution de surveiller les événements ainsi n'est pas forcément la plus efficace . Plusieurs objections peuvent être levées :
- Un poste client non connecté via le programme ne sera pas averti
- On n'obtiendra pas l'identification de l'enregistrement ayant provoqué l'événement
- On ne pourra pas faire de choix . Dans le scenario « Bon d'achats »exposé plus haut , il serait par exemple impossible pour un client de n'être averti que pour son département (sauf à créer des événements distincts , ce qui implique de la « lourdeur »)
- En dernier lieu , une opération de masse déclencherai à chaque fois l'événement alors qu'une seule notification en fin suffirai !
XIV-A. Les propriétés▲
|
AutoRegister |
|
|
Connection |
|
|
Events |
|
|
Name |
|
|
Registered |
|
|
Tag |
XIV-B. Les événements▲
|
OnError |
|
|
OnEventAlerter |
XIV-C. Les méthodes▲
|
RegisterEvents |
|
|
UnRegisterEvents |
XIV-D. La pratique ▲
XV. TZSQLMonitor▲
L'objet TZSQLMonitor va nous permettre de tracer tous les opérations qui seront effectuées
XV-A. Les propriétés▲
|
Active |
|
|
AutoSave |
|
|
FileName |
|
|
MaxTraceCount |
|
|
Name |
|
|
Tag |
XV-B. Les événements▲
|
OnLogTrace |
Les deux permettent d'indiquer ce qu'il faut faire en cas d'événement traçable . La seule différence est la possibilité de filtrer le log , via son type, dans le second . |
|
OnTrace |
procedure TForm1.ZSQLMonitor1LogTrace(Sender: TObject;
Event: TZLoggingEvent);
begin
Memo1.lines.add(Event.AsString);
end;
procedure TForm1.ZSQLMonitor1Trace(Sender: TObject;
Event: TZLoggingEvent; var LogTrace: Boolean);
begin
LogTrace:= Event.Category=lcOther; // ne trace que les events de catégorie autre
end;Ce code nous amènera à parler des types TZLoggingEvent, TZLoggingCategory et TZLoggingFormatter dans le coin du mécano .
XV-C. Les méthodes▲
LogEvent(Event : TZLoggingEvent)
Save
SaveToFile
TraceCount
TraceList[index]
LoggingFormatter
La pratique
Le coin du mécano
XVI. La gestion des erreurs▲
XVII. Appendice A : Structure de la Base de Données exemple▲
J'ai longtemps hésité sur le choix à faire :
- utiliser la base exemple fournie avec la bibliothèque ZEOS
- utiliser une autre base comme celle proposée par SQLPro (voir « En complément sur Developpez.com » à la fin du tutoriel)
qui ont l'avantage de proposer des scripts pour chaque SGBD .
J'ai finalement opté pour une base de données sans trop de prétentions (la structure n'en est certainement pas optimale) pour pouvoir y travailler également des images (blobs) . Je vous présente le « Firebird Judo Club » , qui va nous servir de terrain (devrais-je dire Tatami) d'entraînement !

/********************* ROLES **********************/
CREATE ROLE RDB$ADMIN;
/********************* UDFS ***********************/
/****************** GENERATORS ********************/
/******************** DOMAINS *********************/
/******************* PROCEDURES ******************/
/******************** TABLES **********************/
CREATE TABLE CATEGORIES
(
CODEINTERNE Integer NOT NULL,
AGE Integer NOT NULL,
LIBELLE Varchar(30)
);
CREATE TABLE CEINTURES
(
RANG Smallint NOT NULL,
DAN Smallint NOT NULL,
LIBELLE Varchar(30),
CONSTRAINT PK_CEINTURES PRIMARY KEY (RANG,DAN)
);
CREATE TABLE CLASSEMENT_COMPETITION
(
RANG Integer NOT NULL,
LIBELLE Varchar(20),
MEDAILLE Varchar(10)
);
CREATE TABLE COMPETITIONS
(
ID Bigint NOT NULL,
EVENEMENT Bigint NOT NULL,
PARTICIPANT Bigint NOT NULL,
CLASSEMENT Smallint
);
CREATE TABLE COTISATIONS
(
CODEUNIQUE Bigint NOT NULL,
NUMEROCARTE Bigint NOT NULL,
CODEPERIODE Integer NOT NULL,
MONTANT_PAYE Numeric(15,2) DEFAULT 0.00,
ASSURANCE Smallint DEFAULT 0,
PASSEPORT Smallint DEFAULT 0,
CERTIFICAT_MEDICAL Smallint DEFAULT 0
);
CREATE TABLE EVENEMENTS
(
CODE Bigint NOT NULL,
PERIODE Bigint NOT NULL,
TYPE_EVENT Smallint,
NOM Varchar(40),
LIEU Varchar(40),
MONTANT Numeric(15,2),
AGE_MINI Smallint,
AGE_MAXI Smallint,
CEINTURE_MINI Smallint,
CEINTURE_MAXI Smallint,
DATE_EVENEMENT Date,
CONSTRAINT PK_EVENEMENT PRIMARY KEY (CODE)
);
CREATE TABLE FONCTIONS
(
CODE Integer NOT NULL,
LIBELLE Varchar(30)
);
CREATE TABLE MEMBRES
(
NUMEROCARTE Bigint NOT NULL,
NOM Varchar(30),
PRENOM Varchar(30),
ADRESSE_1 Varchar(38),
ADRESSE_2 Varchar(38),
ADRESSE_3 Varchar(38),
ADRESSE_4 Varchar(38),
CODE_POSTAL Varchar(5),
VILLE Varchar(38),
CEINTURE Smallint,
PASSEPORT Varchar(20) CHARACTER SET NONE,
DATE_NAISSANCE Date,
TELEPHONE Varchar(15),
A_CONTACTER Blob sub_type 1,
PHOTO Blob sub_type 0
);
CREATE TABLE PERIODES
(
CODE_PERIODE Bigint NOT NULL,
DATE_DEBUT Date NOT NULL,
DATE_FIN Date NOT NULL,
LIBELLE Varchar(30)
);
/********************* VIEWS **********************/
/******************* EXCEPTIONS *******************/
/******************** TRIGGERS ********************/
UPDATE RDB$RELATION_FIELDS set RDB$DESCRIPTION = 'Servira de clé unique' where RDB$FIELD_NAME = 'CODEINTERNE' and RDB$RELATION_NAME = 'CATEGORIES';
UPDATE RDB$RELATION_FIELDS set RDB$DESCRIPTION = 'Age maximum de la catégorie au 31.12 de l'année en cours' where RDB$FIELD_NAME = 'AGE' and RDB$RELATION_NAME = 'CATEGORIES';
UPDATE RDB$RELATION_FIELDS set RDB$DESCRIPTION = 'Nom de la catégorie' where RDB$FIELD_NAME = 'LIBELLE' and RDB$RELATION_NAME = 'CATEGORIES';
UPDATE RDB$RELATIONS set
RDB$DESCRIPTION = 'La cétagorie dépend de l''âge du judoka'
where RDB$RELATION_NAME = 'CATEGORIES';
UPDATE RDB$RELATION_FIELDS set RDB$DESCRIPTION = 'Niveau du Judoka' where RDB$FIELD_NAME = 'RANG' and RDB$RELATION_NAME = 'CEINTURES';
UPDATE RDB$RELATION_FIELDS set RDB$DESCRIPTION = 'Le Dan ne devrait servir qu''à la ceinture noire
Dans notre cas , il servira aussi pour indiquer les ceintures intermédiaires pour les jeunes judokas' where RDB$FIELD_NAME = 'DAN' and RDB$RELATION_NAME = 'CEINTURES';
UPDATE RDB$RELATION_FIELDS set RDB$DESCRIPTION = 'Nom de la ceinture ' where RDB$FIELD_NAME = 'LIBELLE' and RDB$RELATION_NAME = 'CEINTURES';
UPDATE RDB$RELATIONS set
RDB$DESCRIPTION = 'Ceintures des judokas'
where RDB$RELATION_NAME = 'CEINTURES';
UPDATE RDB$RELATION_FIELDS set RDB$DESCRIPTION = '0 - Entrainements
1 - Compétitions Inter-Clubs
2 - Compétitions officielles ' where RDB$FIELD_NAME = 'TYPE_EVENT' and RDB$RELATION_NAME = 'EVENEMENTS';
UPDATE RDB$RELATION_FIELDS set RDB$DESCRIPTION = 'Catégorie minimale concernée' where RDB$FIELD_NAME = 'AGE_MINI' and RDB$RELATION_NAME = 'EVENEMENTS';
UPDATE RDB$RELATION_FIELDS set RDB$DESCRIPTION = 'Niveau minimal' where RDB$FIELD_NAME = 'CEINTURE_MINI' and RDB$RELATION_NAME = 'EVENEMENTS';
UPDATE RDB$RELATION_FIELDS set RDB$DESCRIPTION = 'En cas de compétition date unique
En cas d''entrainement , seul le jour sera pris en compte' where RDB$FIELD_NAME = 'DATE_EVENEMENT' and RDB$RELATION_NAME = 'EVENEMENTS';
GRANT DELETE, INSERT, REFERENCES, SELECT, UPDATE
ON CATEGORIES TO SYSDBA WITH GRANT OPTION;
GRANT DELETE, INSERT, REFERENCES, SELECT, UPDATE
ON CEINTURES TO SYSDBA WITH GRANT OPTION;
GRANT DELETE, INSERT, REFERENCES, SELECT, UPDATE
ON CLASSEMENT_COMPETITION TO SYSDBA WITH GRANT OPTION;
GRANT DELETE, INSERT, REFERENCES, SELECT, UPDATE
ON COMPETITIONS TO SYSDBA WITH GRANT OPTION;
GRANT DELETE, INSERT, REFERENCES, SELECT, UPDATE
ON COTISATIONS TO SYSDBA WITH GRANT OPTION;
GRANT DELETE, INSERT, REFERENCES, SELECT, UPDATE
ON EVENEMENTS TO SYSDBA WITH GRANT OPTION;
GRANT DELETE, INSERT, REFERENCES, SELECT, UPDATE
ON FONCTIONS TO SYSDBA WITH GRANT OPTION;
GRANT DELETE, INSERT, REFERENCES, SELECT, UPDATE
ON MEMBRES TO SYSDBA WITH GRANT OPTION;
GRANT DELETE, INSERT, REFERENCES, SELECT, UPDATE
ON PERIODES TO SYSDBA WITH GRANT OPTION;XVIII. Appendice B : Composants d'accès au données Delphi 7 ▲
Tableau Comparatif
|
Composants
|
Composants BDE |
Composants ADO |
Composants
|
|---|---|---|---|
|
TIBDataBase |
TDataBase |
TADOConnection |
TSQLConnection |
|
TIBTable |
TTable |
TADOTable |
TSQLTable |
|
TIBQuery |
TQuery |
TADOQuery |
TSQLQuery |
|
TIBStoredProc |
TStoredProc |
TADOStoredProc |
TSQLStoredProc |
|
TIBDataset |
TADODataset |
TSQLDataset |




