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=Dataset1
Une 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-dessus
Ce 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
UNETABLE
Mais 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
'
;
END
Tous 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 |