Difference between revisions of "SQLdb Tutorial2/fr"

From Lazarus wiki
Jump to navigationJump to search
m (Fixed syntax highlighting)
 
(18 intermediate revisions by one other user not shown)
Line 10: Line 10:
 
Comme indiqué, des applications "réelles" laissent normalement les utilisateurs spécifier leur propres nom d'utilisateur et mot de passe.
 
Comme indiqué, des applications "réelles" laissent normalement les utilisateurs spécifier leur propres nom d'utilisateur et mot de passe.
  
Changeons la fiche de façon à popuvoir les spécifier : ajoutez deux [[TEDit/fr|TEdits]] depuis l'onglet standard. Mettez leur propriété Name à 'Username' et 'Password'.
+
Changeons la fiche de façon à pouvoir les spécifier : ajoutez deux [[TEdit/fr|TEdits]] depuis l'onglet standard. Mettez leur propriété Name à 'Username' et 'Password'.
 
Définissez la propriété PasswordChar de 'Password' à * (l'astérisque) par sécurité vis à vis de personne qui regarderait par dessus votre épaule.
 
Définissez la propriété PasswordChar de 'Password' à * (l'astérisque) par sécurité vis à vis de personne qui regarderait par dessus votre épaule.
  
Si vous voulez rendre plus facile (et moins sécurisé, bien sûr) de se connecter, vous pouvez définir la propriété Text de UserName comme utilisateur valide de la base de données, tel que SYSDBA. Vous pouvez même définir la propriété Text de Password à une valeur par défaut telle que masterkey, simple pour le test sur une machine de développement si la sécurité n'est pas une préoccupation...
+
Si vous voulez rendre plus facile (et moins sécurisé, bien sûr) de se connecter, vous pouvez définir la propriété Text de UserName avec un nom d'utilisateur valide de la base de données, tel que SYSDBA. Vous pouvez même définir la propriété Text de Password à une valeur par défaut telle que masterkey, simple pour le test sur une machine de développement si la sécurité n'est pas une préoccupation...
  
 
Cosmétiquement, l'ajout de quelques étiquettes (''Label'') pour que les gens sachent ce qu'ils sont censés taper est utile.
 
Cosmétiquement, l'ajout de quelques étiquettes (''Label'') pour que les gens sachent ce qu'ils sont censés taper est utile.
Line 26: Line 26:
  
 
Ajoutez le code :
 
Ajoutez le code :
<syntaxhighlight>
+
<syntaxhighlight lang=pascal>
 
procedure TForm1.Button1Click(Sender: TObject);
 
procedure TForm1.Button1Click(Sender: TObject);
 
begin
 
begin
Line 60: Line 60:
  
 
Le code ressemblera à quelque chose comme cela :
 
Le code ressemblera à quelque chose comme cela :
<syntaxhighlight>
+
<syntaxhighlight lang=pascal>
 
procedure TForm1.Button1Click(Sender: TObject);
 
procedure TForm1.Button1Click(Sender: TObject);
 
begin
 
begin
Line 82: Line 82:
  
 
=== Filtrage de données ===
 
=== Filtrage de données ===
Often, tables contain a huge amount of data that the user doesn't want to see (and that might take a long time to query from the database and travel over the network). Let's assume that only the customers from the USA should be displayed. Therefore the SQL instruction in 'SQLQuery1' would look like:
+
Souvent, une table contient une énorme quantité de données que l'utilisateur ne veut(ou ne doit) pas voir (et cela prend beaucoup de temps à requêter et voyage à travers le réseau). Supposons que seuls les clients des USA devraient être affichés. En conséquence, l'ordre SQL dans 'SQLQuery1' ressemblera à quelque chose comme ceci :
 
<syntaxhighlight lang="SQL">
 
<syntaxhighlight lang="SQL">
 
select * from CUSTOMER where COUNTRY = 'USA'
 
select * from CUSTOMER where COUNTRY = 'USA'
 
</syntaxhighlight>
 
</syntaxhighlight>
... which would translate to something like this in our code:
+
... qui devrait être transformé en quelque chose comme cela dans notre code :
<syntaxhighlight>
+
<syntaxhighlight lang=pascal>
 
SQLQuery1.SQL.Text := 'select * from CUSTOMER where COUNTRY = 'USA'';
 
SQLQuery1.SQL.Text := 'select * from CUSTOMER where COUNTRY = 'USA'';
 
</syntaxhighlight>
 
</syntaxhighlight>
There are two reasons why we will not use this instruction for our example application:  
+
Il y a deux raisons pour lesquelles nous n'utilisaeraon pas cette ordre dans notre exemple d'application :
  
First there is a problem with the usage of the single quote. The compiler would interpret the quote before USA as a closing quote (the first quote is before the select from...) and so the SQL instruction would become invalid. Solution: double the inside quotes: <syntaxhighlight>SQLQuery1.SQL.Text := 'select * from CUSTOMER where COUNTRY = ''USA''';</syntaxhighlight>
+
D'abord il y a un problème avec l'emploi des aapostrophes. Le compilateur interprétera cela l'apostrophe avant USA comme une apostrophe de fermeture (la première apostrophe est devant le SELECT * FROM...), ainsi l'ordre SQL va devenir invalide. Solution : il faut doubler les apostrophes intérieures :
 +
<syntaxhighlight lang=pascal>SQLQuery1.SQL.Text := 'select * from CUSTOMER where COUNTRY = ''USA''';</syntaxhighlight>
  
The second, more important reason is the fact, that we probably don't know what constraints the user will want to filter on. We don't want to limit the flexibility of the user.  
+
Ensuite, raison plus importante dans les faits, nous ignorons probablement ce quelles contraintes l'utilisateur voudra filtrer. Nous ns voulons pas limiter la flexibilité de l'utilisateur.
  
To get this flexibility, first we change our SQL query statement and replace 'USA' by a placeholder (a parameter in SQL speak): change the Button1click procedure and replace
+
Pour obtenir cette flexibilité, nous changeons en premier notre ordre SQL et remplaçons 'USA' par une marque substitutive (''placeholder'', un paramètre pour parler SQL) : changez la procédure Button1click et remplacez :
<syntaxhighlight>
+
<syntaxhighlight lang=pascal>
 
SQLQuery1.SQL.Text := 'select * from CUSTOMER';
 
SQLQuery1.SQL.Text := 'select * from CUSTOMER';
 
</syntaxhighlight>
 
</syntaxhighlight>
with
+
par :
<syntaxhighlight>
+
<syntaxhighlight lang=pascal>
 
SQLQuery1.SQL.Text:= 'select * from CUSTOMER where COUNTRY = :COUNTRY';
 
SQLQuery1.SQL.Text:= 'select * from CUSTOMER where COUNTRY = :COUNTRY';
 
</syntaxhighlight>
 
</syntaxhighlight>
  
In FPC SQLDB, the SQL parameter is marked by the leading colon (other languages/environments use other conventions like ?). To allow the user to enter a value for the filter, we place a ''TEdit'' component on our form. Delete the value of its 'Text' property.
+
Dasn SQLDB de FPC, le paramètre SQL est marqué par un caractère deux-points (d'autres langages/environnements utilisent une autre convention comme ?). Pour permettre à l'utilisateur d'entrer une valeur pour le filtre, nous plaçons un composant ''TEdit'' sur notre fiche. Supprimez a valeur dans la propriété 'Text'.
 
+
Nous pouvons maintenant prendre le texte entré dans le TEdit et remplir le paramètre SQL COUNTRY en utilisant la propriété 'Params' du TSQLQuery. Ajoutez ce qui suit dans l'instruction précédente :
We can now take the text entered in the TEdit and fill the SQL COUNTRY parameter by using the 'Params' property of TSQLQuery. Add this below the previous statement:
+
<syntaxhighlight lang=pascal>
<syntaxhighlight>
 
 
SQLQuery1.Params.ParamByName('COUNTRY').AsString := Edit1.Text;
 
SQLQuery1.Params.ParamByName('COUNTRY').AsString := Edit1.Text;
 
</syntaxhighlight>
 
</syntaxhighlight>
The parameter can be specified by its position or name. Using the name should improve the readability of the source code, and obviously helps if you insert more parameters in the middle of existing parameters.
+
Le paramètre peut être spécifié par sa position ou son nom. L'utilisation du nom améliorera la lisibilité du code source, et évidemment aide si vous insérez plus de paramètre au milieu des paramètres existants.  
 
 
We use .AsString to assign a string value to the parameter; there are equivalent property assignments for integer parameters, boolean parameters etc.
 
  
The code up to now forces us to use a filter. If a user specifies an empty value in the edit box, no record will be displayed. This is probably not what we want. Let's test for an empty value and build our query accordingly. We should end up with a procedure like this:
+
Nous utilisons '.AsString' pour affecter une valeur chaîne au paramètre ; il y a des affectation de propriété équivalente pour les paramètres entiers, booléens etc.
  
<syntaxhighlight>
+
Jusqu'à maintenant, le code nous forçe à utiliser un filtre. Si un utilisateur spécifie une valeur vide dans la zone de saisie, aucun enregistrement ne sera affiché. Ce n'est sans doute pas ce que nous voulons. Testons la valeur vide et construisons la requête en conséquence. Nous devrions nous retrouver avec une procédure comme celle-ci :
 +
<syntaxhighlight lang=pascal>
 
procedure TForm1.Button1Click(Sender: TObject);
 
procedure TForm1.Button1Click(Sender: TObject);
 
begin
 
begin
Line 149: Line 148:
 
end;                                             
 
end;                                             
 
</syntaxhighlight>
 
</syntaxhighlight>
Now you can play around a bit with filtering using Edit1. If you enter a country that's not present in the database, an empty grid is shown.
+
Maintenant vous pouvez jouer un peu avec le filtrage en utilisant Edit1. Si vous entrez un pays qui n'est pas présent dans la base de données, une grille affichée est vide.
  
 
=== Gestion des erreurs ===
 
=== Gestion des erreurs ===
The application should run, but sometimes problems can occur.
+
L'application devrait tourner mais parfois des problèmes arrivent. Les bases de données, même les bases incorporées, peuvent planter (i.e. quand le serveur de base de données plante, le disque est plein, ou juste provoqué par un bug), laissant l'application en suspens.
Databases, even embedded databases can crash (e.g. when the database server crashes, the disk is full, or just due to a bug), leaving the application hanging.
 
  
Access to a database (any external process, really) should therefore '''always''' be integrated in a try ... except and/or try ... finally construct. This ensures that database errors are handled and the user isn't left out in the cold. A rudimentary routine for our example application could look like this:
+
L'accès à une base de données (tout processus externe, en vrai) devrait donc ''toujours'' être intégré dans une construction try ... except et/ou try ... finally. Cela garantit que les erreurs de bases de données sont traitées et que l'utilisateur n'est pas laissé à l'abandon. Une routine rudimentaire pour notre exemple peut ressembler à ceci :
<syntaxhighlight>
+
<syntaxhighlight lang=pascal>
 
begin
 
begin
 
   try
 
   try
Line 174: Line 172:
  
 
==== SQLite, PostgreSQL, autres bases de données ====
 
==== SQLite, PostgreSQL, autres bases de données ====
You can either use the more generic EDatabaseError, or - if available - your own specialized databaseerror, if you need more details.
+
Vous pouvez soit utiliser la plus générique EDatabaseError, ou, si disponible, votre propre dérivé de EDatabaseError, si vous avez besoin de plus de détail.
E.g. SQLite and the PostgreSQL driver in FPC 2.6.1 and lower doesn't have a specialized E*DatabaseError; you'd have to use EDatabaseError. PostgreSQL on FPC trunk (development version) has EPQDatabaseError.
+
P.ex. SQLite et le pilote PostgreSQL dans FPC 2.6.1 et antérieur n'ont pas de E*DatabaseError spécialisé; vous devez utiliser EDatabaseError. PostgreSQL sur FPC du tronc (version de développement) a un dérivé EPQDatabaseError.
  
 
== Edition des données utilisant la grille ==
 
== Edition des données utilisant la grille ==
  
 
=== Edition ===
 
=== Edition ===
Up to now, if you tried to edit data in the grid, the changes would not be saved. This is because the ''SQLQuery1'' is not instructed to send the changes to the database transaction at the right moment.
+
Jusqu'à maintenant, si vous essayiez d'éditer des données dans la grille, les modifications ne pourront pas être enregistrés. Ceci parce que le ''SQLQuery1'' n'est pas construit pour envoyer les changements vers la transaction de base de données au bon moment.
We need to fix this, and then commit the transaction in the database, so all changes get written. For this, you would use code like this:
+
 
<syntaxhighlight>
+
Nous avons besion de corriger cela et donc de faire le commit de la base de données, ainsi tous les changements seront écrit. Pour ceci, vous devrez utiliser du code comme ceci :
 +
<syntaxhighlight lang=pascal>
 
SQLQuery1.ApplyUpdates; //Pass user-generated changes back to database...
 
SQLQuery1.ApplyUpdates; //Pass user-generated changes back to database...
 
SQLTransaction1.Commit; //... and commit them using the transaction.
 
SQLTransaction1.Commit; //... and commit them using the transaction.
 
//SQLTransaction1.Active now is false
 
//SQLTransaction1.Active now is false
 
</syntaxhighlight>
 
</syntaxhighlight>
We want to make sure any edits (inserts, updates, deletes) are written to the database:
+
Nous voulons garantir que toutes les éditions (insertions, mises à jour, suppressions) sont écrites dans la base de données :
* when the users changes the filtering criteria and presses the button to query the database
+
* quand l'utilisateur change le critère de filtrage et presse le bouton poutr interroger la base de données.
* when the form is closed
+
* quand la fiche est fermée.
  
It makes sense to make a separate procedure for this that is called in those two instances.
+
Il est pertinent de faire une procédure séparée pour ceci qui est appelée pour ces deux instances.  
Go to the code, and add an empty line here:
+
Allez au code et ajoutez une ligne vide ici :
<syntaxhighlight>
+
<syntaxhighlight lang=pascal>
 
   TForm1 = class(TForm)
 
   TForm1 = class(TForm)
 
     Button1: TButton;
 
     Button1: TButton;
Line 207: Line 206:
 
   private
 
   private
 
</syntaxhighlight>
 
</syntaxhighlight>
then type
+
puis tapez :
<syntaxhighlight>
+
<syntaxhighlight lang=pascal>
 
     procedure SaveChanges;
 
     procedure SaveChanges;
 
</syntaxhighlight>
 
</syntaxhighlight>
press shift-ctrl-c (default combination) to let code completion automatically create the corresponding procedure body.
+
pressez shift-ctrl-c (combinaison par défaut) pour laisser la complétion de code créer le corps de la procédure correspondante.
  
We need to add error handling and check that the transaction is active - remember, this code also gets called when pressing the button the first time, when the transaction is not active yet. We get:
+
Nous avons besoin d'ajouter la gestion d'erreur et contrôler que la transaction est active - souvenez-vous, ce code est aussi appelé quand le bouton est actionné la première fois, quand la transaction n'est pas encore active. Nous obtenons :
<syntaxhighlight>
+
<syntaxhighlight lang=pascal>
 
procedure Tform1.SaveChanges;
 
procedure Tform1.SaveChanges;
 
// Saves edits done by user, if any.
 
// Saves edits done by user, if any.
Line 238: Line 237:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
Now we need to call this procedure at the appropriate moments:
+
Maintenant nous avons besoin d'appeler cette procédure aux moments appropriés :
<syntaxhighlight>
+
<syntaxhighlight lang=pascal>
 
procedure Tform1.Button1click(Sender: TObject);
 
procedure Tform1.Button1click(Sender: TObject);
 
begin
 
begin
Line 247: Line 246:
 
....
 
....
 
</syntaxhighlight>
 
</syntaxhighlight>
and
+
et
<syntaxhighlight>
+
<syntaxhighlight lang=pascal>
 
procedure Tform1.Formclose(Sender: TObject; var Closeaction: Tcloseaction);
 
procedure Tform1.Formclose(Sender: TObject; var Closeaction: Tcloseaction);
 
begin
 
begin
Line 256: Line 255:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
Now test and see if edits made in the dbgrid are saved to the database.
+
Maintenant testez et voyez si les modifications réalisées dans la grille sont enregistrées dans la base de données.
  
 
=== Masquage de la colonne de clé primaire ===
 
=== Masquage de la colonne de clé primaire ===
Often, you don't want your users to see autonumber/generated primary keys as they are only meant to maintain referential integrity. If users do see them, they might want to try the edit the numbers, get upset that the numbers change, that there are gaps in the numbers, etc.
+
Souvent, vous ne voulez pas que vos utilisateurs voient les clés primaires auto-incrémentées car elles ne servent qu'à maintenir l'intégrité référentielle. Si les utilisateurs les voient, ils voudront peut-être essayer de modifier les numéros, se fâcher lorsque les chiffres changent, qu'il y ait des lacunes dans les chiffres, etc.
  
In our example, CUST_NO is the primary key, with content auto-generated by Firebird using triggers and a sequence/generator. This means that you can insert a new record without specifying the CUST_NO; Firebird will create one automatically.
+
Dans notre exemple, CUST_NO est la clé primaire, avec du contenu auto-généré par Firebird en utilisant des triggers et un générateur (séquence). ,Ceci veut dire que vous pouvez insérer un nouvel enregistrement sans spécifier le champ CUST_NO ; Firebird créera la valeur du champ automatiquement.
  
We could simply change our SQLQuery1.SQL.Text property to not include CUST_NO, but this would lead to problems when editing data - a primary key is needed in those circumstances for uniquely identifying the row/record in question.
+
Nous pouvons changer simplement notre propriété SQLQuery1.SQL.Text pour ne pas inclure CUST_NO, mais cela amènera à des problèmes lors de l'édition des données - la clé primaire est demandée pour identifier l'enregistement/ ligne en question.
  
Therefore, let's use a trick to query for all columns/fields in the table, but keep the grid from showing the first field, CUST_NO: in the Button1Click procedure, add code so it looks like:
+
Donc utilisons une astuce pour récupérer toutes les colonnes/champs mais empêcher la grille d'afficher le premier champ, CUST_NO : dans la procédure Button1Click, ajoutez le code comme cela :
<syntaxhighlight>
+
<syntaxhighlight lang=pascal>
 
procedure Tform1.Button1click(Sender: TObject);
 
procedure Tform1.Button1click(Sender: TObject);
 
begin
 
begin
Line 275: Line 274:
 
     DBGrid1.Columns[0].Visible:=false;
 
     DBGrid1.Columns[0].Visible:=false;
 
</syntaxhighlight>
 
</syntaxhighlight>
Recompile, and check to see if the primary key column is really hidden.
+
Recompilez et contrôlez pour voir si la colonne de clé primaire est effectivement cachée.
  
 
==== SQLite, autres bases de données ====
 
==== SQLite, autres bases de données ====
* Other databases: a lot of other databases use an 'autonumber' or 'autoinc' type of field to provide auto-generated field content. Try changing your table definition and see if it works.
+
* Autres base de données : de nombreuses autres bases de données utilisent un type de champ 'autonumber' ou 'autoinc' pour fournir du contenu de champ auto-généré. Essayez de changer votre définition de table pour voir si cela fonctionne.
* Sqlite: the example above works for SQLite as is because we're using an integer primary key. See the [http://www.sqlite.org/autoinc.html documentation] for details.
+
* Sqlite : l'exmple du dessus fonctionne pour SQLite car nous utilisons une clé primaire entière. Voir la the [http://www.sqlite.org/autoinc.html documentation] pour des détails.
  
 
=== Insertion de nouvelles données ===
 
=== Insertion de nouvelles données ===
If you insert new rows/records without any CUST_NO information you may have noticed that you get an error message: <tt>Field CUST_NO is required, but not supplied</tt>. This also happens if you hid the CUST_NO column, as in the previous section.
+
Si vous insérez de nouveaux enregistements/lignes sans aucune information CUST_NO, vous pouvez avoir remarqué que vous obtenez le message d'erreur : <tt>Field CUST_NO is required, but not supplied</tt>. Ceci survient aussi si vous cachez la colonne CUST_NO comme dans la section précédente.
  
The reason: Lazarus thinks that CUST_NO is required. That's not so strange, because it is a primary key and the underlying table definition in the database does say it is required.
+
La raison : Lazarus pense que CUST_NO est requis. Cela n'est pas étrange, car c'est une clé primaire et la définition de table sous-jacente dans la base de données dit qu'il est requis.
  
If we can instruct Lazarus that this field is not actually required, we can pass empty values (=NULL values) to the database.
+
Si nous pouvons intimer à Lazarus que ce champ n'est pas actuellement requis, nous pouvons passer des valeurs vides (=valeurs à NULL) à la base de données.
Fortunately, a query's field object has a ''Required'' property that does exactly that.
+
Heureusement, un objet champ de requête a une propriété ''Required'' qui sert exactement à cela.
  
Change the code to something like:
+
Modifiez le code en quelque chose comme :
<syntaxhighlight>
+
<syntaxhighlight lang=pascal>
 
     SQLQuery1.Open;
 
     SQLQuery1.Open;
 
     {
 
     {
Line 305: Line 304:
  
 
=== Suppression de données ===
 
=== Suppression de données ===
You can let your users use the mouse to do this. You don't even need to code a single line for this functionality...
+
Vous pouvez laisser les utilisateurs employer la souris pour cela. Vous n'avez même pas besoin de code une seule ligne pour cette fonctionnalité.
 +
 
 +
Sur l'onglet 'Data controls', sélectionner le composant ''TDBNavigator'' et posez-le sur la fiche, au dessus de la grille.
  
On the 'Data Controls' tab, select a ''TDBNavigator'' component and drop it on the form, above the grid.
+
Pour indiquer à quoi est relié le navigateur, mettez votre composant DataSource (DataSource1) dans sa propriété DataSource en utilisant l'inspecteur d'objet.
  
To indicate what the navigator should be linked to, set its ''DataSource'' property to your existing datasource ('DataSource1') using the Object Inspector.
+
Maintenant vous pouvez utiliser le bouton sur le ''DBNavigator'' pour supprimer des enregistrements, mais aussi pour en insérer et vous déplacer parmi les enregistrements. Aussi, en éditant les cellules/champs, vous pouvez utiliser le bouton ''Cancel'' pour annuler vos éditions.
Now you can use the button on the ''DBNavigator'' to delete records, but also insert them, and move around the records. Also, when editing cells/fields, you can use the ''Cancel'' button to cancel your edits.
 
  
To allow users to delete the row they're in on the grid using the {{keypress|Delete}} key, add ''LCLType'' (this contains defintions for key codes) to your uses clause:
+
Pour permettre à l'utilisateur de supprimer la ligne sur laquelle ils sont dans la grille en utilisant la touche {{keypress|Delete}}, ajoutez l'unité ''LCLType'' (elle contient les définitions des codes de touche) à votre clause uses :
<syntaxhighlight>
+
<syntaxhighlight lang=pascal>
 
uses
 
uses
 
   Classes, SysUtils, sqldb, pqconnection, DB, FileUtil, Forms,
 
   Classes, SysUtils, sqldb, pqconnection, DB, FileUtil, Forms,
Line 319: Line 319:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
... then handle the KeyUp event for the grid, which occurs when a key is released if in the grid. However, we do need to check that the user is not editing a field - as he'll probably use the {{keypress|Delete}} key to delete letters rather than the record he's working on.
+
... il faut coder l'événement KeyUppour la grille, qui survient quand une touche est relâchée dans la grille. Toutefois; nous avons besoin de contrôler que l'utilisateur n'est pas en train de modifier un champ - comme il peut utiliser la touche {{keypress|Delete}} pour supprimer des lettres plutôt que l'enregistrement sur lequel il travaille.
  
Select the grid, then go to events and create an OnKeyUp event like this:
+
Sélectionnez la grille et allez sur les événements pour créer l'événement OnKeyUp comme ceci :
<syntaxhighlight>
+
<syntaxhighlight lang=pascal>
 
procedure TForm1.DBGrid1KeyUp(Sender: TObject; var Key: Word; Shift: TShiftState
 
procedure TForm1.DBGrid1KeyUp(Sender: TObject; var Key: Word; Shift: TShiftState
 
   );
 
   );
Line 337: Line 337:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
{{Note| By default TDBGrid property Options / dgDisableDelete is set to false, this means a user can delete any record with the {{keypress|ctrl}}-{{keypress|delete}} key combo. You may not want this behaviour.}}
+
{{Note| Par défaut, dans la propriété Options du TDBGrid, dgDisableDelete est mise à False, ce qui veut dire que l'utilisateur peut supprimer un enregistrement avec  la combinaison {{keypress|ctrl}}+{{keypress|delete}}. Vous pouvez ne pas désirer ce comportement.}}
  
 
== Résumé ==
 
== Résumé ==
If you followed along up to now, you can retrieve data from the database, filter it, and edit and delete data in the grid. Your code should look something like this:
+
Si vous avez tout suivi jusqu'à maintenant, vous pouvez récupérer des données de la base, les filtrer, les modifier, les supprimer dans la grille. Votre code ressemble à quelque chose comme cela.
<syntaxhighlight>
+
<syntaxhighlight lang=pascal>
 
unit sqldbtutorial1unit;
 
unit sqldbtutorial1unit;
  
Line 496: Line 496:
  
 
=== Firebird sur Windows ===
 
=== Firebird sur Windows ===
A bonus for Firebird users on Windows: if you have been following this tutorial (even if you only did the basic example), you renamed the <tt>fbembed.dll</tt> embedded Firebird library to <tt>fbclient.dll</tt>. With this, Lazarus could connect to regular Firebird servers (either on another machine or on your local machine).
+
Un bonus pour les utilisateurs de Firebird sur Windows ; si vous avez suivi ce tutoriel (même si vous n'avez fait que l'exemple de base), vous avez renommé la bibliothèque incorporée de Firebird <tt>fbembed.dll</tt> en <tt>fbclient.dll</tt>. avec ceci, Lazarus se connectera aux serveurs Firebird normaux (sur une machine distante ou sur votre machine locale). Toutefois, vous pouvez aussi copier la base de données <tt>employee.fdb</tt> dans votre dossier d'application, exécuter l'application, effacer le TEdit ''Server name'' et utiliser Firebird incorporé pour vous connecter directement au fichier de la base de données, sans configuration de serveur.
However, you can also copy the <tt>employee.fdb</tt> database to your application directory, run the application, clear the ''Server name'' TEdit and use Firebird embedded to directly connect to the database file, without any servers set up.
 
  
This is great if you want to deploy database applications to end users, but don't want the hassle of installing servers (checking if a server is already installed, if it's the right version, having users check firewalls, etc).
+
Ceci est génial si vous souhaitez déployer des applications de base de données vers les utilisateurs finaux, mais ne souhaitez pas avoir du mal à installer des serveurs (vérifier si un serveur est déjà installé, s'il s'agit de la bonne version, faire en sorte que les utilisateurs vérifient les pare-feu, etc.).
  
See [[Firebird embedded]] for more details.
+
Voir [[Firebird embedded/fr|Firebird incorporé]] pour plus de détails.
  
September 2011: in recent development (SVN) versions of Free Pascal, FPC tries to first load <tt>fbembed.dll</tt>, so you need not rename <tt>fbclient.dll</tt> anymore for this to work.
+
Note (non vérifiée) : Depuis 2011, FPC tente de charger d'abord <tt>fbembed.dll</tt>, donc vous n'avez pas besoin de renommer <tt>fbclient.dll</tt> pour que cela fonctionne.
  
 
=== Firebird sur Linux/OSX/Unix ===
 
=== Firebird sur Linux/OSX/Unix ===
There must be a way to get this to work on Linux/OSX. See [[Firebird]] for hints and links. Updates to the wiki are welcome.
+
Il doit y avoir un moyen pour que cela fonctionne sur Linux / OSX. Voir [[Firebird/fr|Firebird]] pour les conseils et les liens. Les mises à jour du wiki sont les bienvenues.
  
 
=== SQLite ===
 
=== SQLite ===
SQLite certainly offers embedded functionality - it does not allow a client/server setup on the other hand.
+
Certes, SQLite offre une fonctionnalité incorporée mais d'un autre côté, il ne permet pas de configuration client/serveur.
By following the tutorial above, you can see that switching between databases (e.g. SQLite and Firebird) is not so much work at all.
+
En suivant le tutoriel du haut, vous pouvez voir cela qu'en basculant les bases de données (p.ex. SQLite et Firebird) ne fait pas tant de travail que cela.
  
 
=== Autres bases de données ===
 
=== Autres bases de données ===
Your database might offer similar functionality. Updates of this wiki for other database systems are welcome.
+
Votre base de données peut offrir des fonctionnalités similaires. Des mises à jour de ce Wiki pour d'autres SGBD sont les bienvenues.
  
 
== Voir aussi ==
 
== Voir aussi ==
* [[SQLdb Tutorial0]]: Instructions for setting up sample tables/sample data for the tutorial series.
+
* [[SQLdb Tutorial0/fr|SQLdb Tutorial0]] : Instructions pour configurer les tables et les données d'exemple pour la série de tutoriels.
* [[SQLdb Tutorial1]]: First part of the DB tutorial series, showing how to set up a grid that shows database data
+
* [[SQLdb Tutorial1/fr|SQLdb Tutorial1]] : 1re partie de la série de tutoriels sur les bases de données, montrant comment configurer une grille pour afficher les données.
* [[SQLdb Tutorial3]]: Third part of the DB tutorial series, showing how to program for multiple databases and use a login form
+
* [[SQLdb Tutorial2/fr|SQLdb Tutorial2]] : 2e partie de la série de tutoriels, montrant l'édition, l'insertion, etc.
* [[SQLdb Tutorial4]]: Fourth part of the DB tutorial series, showing how to use data modules
+
* [[SQLdb Tutorial3/fr|SQLdb Tutorial3]] : 3e partie de la série de tutoriels, montrant comment développer pour des bases de données multiples et utiliser une fiche de connexion.
* [[Lazarus Database Overview]]: Information about the databases that Lazarus supports. Links to database-specific notes.
+
* [[SQLdb Tutorial4/fr|SQLdb Tutorial4]] : 4e partie de la série de tutoriels, montrant comment utiliser les modules de données.
* [[SQLdb Package]]: information about the SQLdb package
+
* [[Lazarus Database Overview/fr|Vue d'ensemble des bases de données pour Lazarus]] : Information sur les bases de données supportées par Lazarus. Redirige vers des notes spécifiques à certaines bases de données.
* [[SQLdb Programming Reference]]: an overview of the interaction of the SQLdb database components
+
* [[SQLdb Package/fr|Paquet SQLdb]] : Information sur le paquetage SQLdb.
* [[SqlDBHowto]]: information about using the SQLdb package
+
* [[SQLdb Programming Reference/fr|Référence de programmation SQLdb]] : Une vue d'ensemble de l'interaction entre les composants de bases de données SQLdb.
* [[Working With TSQLQuery]]: information about TSQLQuery
+
* [[SqlDBHowto/fr|SqlDBHowto]] : Information sur l'utilisation du paquet SQLdb.
<br/>
+
* [[Working With TSQLQuery/fr|Travailler avec TSQLQuery]] : Informations sur TSQLQuery.
 +
* [http://www.freepascal.org/docs-html/fcl/sqldb/usingparams.html utilisation des paramètres]

Latest revision as of 01:10, 27 February 2020

English (en) français (fr) 日本語 (ja)

Portail de la base de données

Références:

Tutoriels/articles pratiques :

Bases de données

Advantage - MySQL - MSSQL - Postgres - Interbase - Firebird - Oracle - ODBC - Paradox - SQLite - dBASE - MS Access - Zeos

Vue d'ensemble

Si vous avez suivi le Tutoriel SQLdb 1, vous avez un grille basique montrant des informations provenant de la base de données. Maintenant que votre application marche, vous pouvez y ajouter certains raffinements.

Connexion dynamique à une base de données

Jusqu'à maintenant, nous utilisions des nom de base de données, emplacement de base de données, nom d'utilisateur et mot de passe fixé pour simplifier. Comme indiqué, des applications "réelles" laissent normalement les utilisateurs spécifier leur propres nom d'utilisateur et mot de passe.

Changeons la fiche de façon à pouvoir les spécifier : ajoutez deux TEdits depuis l'onglet standard. Mettez leur propriété Name à 'Username' et 'Password'. Définissez la propriété PasswordChar de 'Password' à * (l'astérisque) par sécurité vis à vis de personne qui regarderait par dessus votre épaule.

Si vous voulez rendre plus facile (et moins sécurisé, bien sûr) de se connecter, vous pouvez définir la propriété Text de UserName avec un nom d'utilisateur valide de la base de données, tel que SYSDBA. Vous pouvez même définir la propriété Text de Password à une valeur par défaut telle que masterkey, simple pour le test sur une machine de développement si la sécurité n'est pas une préoccupation...

Cosmétiquement, l'ajout de quelques étiquettes (Label) pour que les gens sachent ce qu'ils sont censés taper est utile.

Aussi pour rendre plus facile la connexion à toute base de données exemple sur tout serveur Firebird/Interbase, nous ajoutons deux zone de texte pour le serveur et le chemin à la base de données. Ajoutons ces deux autres zones de texte et appelons-les ServerName et DatabaseName.

Si vous voulez, vous pouvez définir la propriété 'Text' à des valeurs sensibles dans votre situation, p.ex. localhost et C:\Program Files\Firebird\Firebird_2_5\examples\empbuild\EMPLOYEE.FDB. Des étiquettes (Labels) pour expliquer ce que doit entrer l'utilisateur peuvent aider aussi.

Pour clarifier, nous allons supprimer les informations de connexion de nos composants en conception : Dans le composant TSQLConnector, retirez tout les textes des propriétés UserName, Password, DatabaseName et HostName.

Maintenant, finalement, nous avons besoin d'indiquer à notre composant de connexion à la base de données comment se connecter. Cela est uniquement nécessaire au début de l'exécution de l'application. Dans notre cas, le bouton existant 'Button1' est une bonne manière de mettre en place la connexion.

Ajoutez le code :

procedure TForm1.Button1Click(Sender: TObject);
begin
  SQLQuery1.Close;
  //Connection settings for Firebird/Interbase database
  //only needed when we have not yet connected:
  if not DBConnection.Connected then
  begin
    DBConnection.HostName := ServerName.Text;
    DBConnection.DatabaseName := DatabaseName.Text;
    DBConnection.Username := UserName.Text;
    DBConnection.Password := Password.Text;
    // Now we've set up our connection, visually show that
    // changes are not possibly any more
    ServerName.ReadOnly:=true;
    DatabaseName.ReadOnly:=true;
    UserName.ReadOnly:=true;
    Password.ReadOnly:=true;
  end;  
  SQLQuery1.SQL.Text:= 'select * from CUSTOMER';
  DBConnection.Connected:= True;
  SQLTransaction1.Active:= True;
  SQLQuery1.Open;
end;

Maintenant exécutez et testez si vous êtes connectés.

SQLite, autres bases de données

Adaptez la propriété Text dans le TEdit comme demandé ; p.ex. employee.sqlite pour SQLite.

For SQLite, la spécification des HostName, Username et password n'a pas de sens, donc vous pouvez omettre ces TEddits, commentez l'affectation des valeurs correspondantes dans DBConnection dans le code du dessus. Pour FireBird incorporé, forcez en dur le UserName à SYSDBA ; cela ne fera aucun mal si SQLite est utilisé.

Le code ressemblera à quelque chose comme cela :

procedure TForm1.Button1Click(Sender: TObject);
begin
  SQLQuery1.Close;
  //Connection settings for embedded databases
  //only needed when we have not yet connected:
  if not DBConnection.Connected then
  begin
    DBConnection.DatabaseName := DatabaseName.Text;
	DBConnection.UserName := 'SYSDBA'; //Firebird embedded needs this; doesn't harm if using SQLite
    // Now we've set up our connection, visually show that
    // changes are not possibly any more
    DatabaseName.ReadOnly:=true;
  end;  
  SQLQuery1.SQL.Text:= 'select * from CUSTOMER';
  DBConnection.Connected:= True;
  SQLTransaction1.Active:= True;
  SQLQuery1.Open;
end;

Filtrage de données

Souvent, une table contient une énorme quantité de données que l'utilisateur ne veut(ou ne doit) pas voir (et cela prend beaucoup de temps à requêter et voyage à travers le réseau). Supposons que seuls les clients des USA devraient être affichés. En conséquence, l'ordre SQL dans 'SQLQuery1' ressemblera à quelque chose comme ceci :

select * from CUSTOMER where COUNTRY = 'USA'

... qui devrait être transformé en quelque chose comme cela dans notre code :

SQLQuery1.SQL.Text := 'select * from CUSTOMER where COUNTRY = 'USA'';

Il y a deux raisons pour lesquelles nous n'utilisaeraon pas cette ordre dans notre exemple d'application :

D'abord il y a un problème avec l'emploi des aapostrophes. Le compilateur interprétera cela l'apostrophe avant USA comme une apostrophe de fermeture (la première apostrophe est devant le SELECT * FROM...), ainsi l'ordre SQL va devenir invalide. Solution : il faut doubler les apostrophes intérieures :

SQLQuery1.SQL.Text := 'select * from CUSTOMER where COUNTRY = ''USA''';

Ensuite, raison plus importante dans les faits, nous ignorons probablement ce quelles contraintes l'utilisateur voudra filtrer. Nous ns voulons pas limiter la flexibilité de l'utilisateur.

Pour obtenir cette flexibilité, nous changeons en premier notre ordre SQL et remplaçons 'USA' par une marque substitutive (placeholder, un paramètre pour parler SQL) : changez la procédure Button1click et remplacez :

SQLQuery1.SQL.Text := 'select * from CUSTOMER';

par :

SQLQuery1.SQL.Text:= 'select * from CUSTOMER where COUNTRY = :COUNTRY';

Dasn SQLDB de FPC, le paramètre SQL est marqué par un caractère deux-points (d'autres langages/environnements utilisent une autre convention comme ?). Pour permettre à l'utilisateur d'entrer une valeur pour le filtre, nous plaçons un composant TEdit sur notre fiche. Supprimez a valeur dans la propriété 'Text'. Nous pouvons maintenant prendre le texte entré dans le TEdit et remplir le paramètre SQL COUNTRY en utilisant la propriété 'Params' du TSQLQuery. Ajoutez ce qui suit dans l'instruction précédente :

SQLQuery1.Params.ParamByName('COUNTRY').AsString := Edit1.Text;

Le paramètre peut être spécifié par sa position ou son nom. L'utilisation du nom améliorera la lisibilité du code source, et évidemment aide si vous insérez plus de paramètre au milieu des paramètres existants.

Nous utilisons '.AsString' pour affecter une valeur chaîne au paramètre ; il y a des affectation de propriété équivalente pour les paramètres entiers, booléens etc.

Jusqu'à maintenant, le code nous forçe à utiliser un filtre. Si un utilisateur spécifie une valeur vide dans la zone de saisie, aucun enregistrement ne sera affiché. Ce n'est sans doute pas ce que nous voulons. Testons la valeur vide et construisons la requête en conséquence. Nous devrions nous retrouver avec une procédure comme celle-ci :

procedure TForm1.Button1Click(Sender: TObject);
begin
  SQLQuery1.Close;
  //Connection settings for Firebird/Interbase database
  //only needed when we have not yet connected:
  if not DBConnection.Connected then
  begin
    DBConnection.HostName := ServerName.Text;
    DBConnection.DatabaseName := DatabaseName.Text;
    DBConnection.Username := UserName.Text;
    DBConnection.Password := Password.Text;
    // Now we've set up our connection, visually show that
    // changes are not possibly any more
    ServerName.ReadOnly:=true;
    DatabaseName.ReadOnly:=true;
    UserName.ReadOnly:=true;
    Password.ReadOnly:=true;
  end; 
  // Show all records, or filter if user specified a filter criterium
  if Edit1.Text='' then
    SQLQuery1.SQL.Text := 'select * from CUSTOMER'
  else
  begin
    SQLQuery1.SQL.Text := 'select * from CUSTOMER where COUNTRY = :COUNTRY';
    SQLQuery1.Params.ParamByName('COUNTRY').AsString := Edit1.Text;
  end;
  DBConnection.Connected:= True;
  SQLTransaction1.Active:= True;
  SQLQuery1.Open;
end;

Maintenant vous pouvez jouer un peu avec le filtrage en utilisant Edit1. Si vous entrez un pays qui n'est pas présent dans la base de données, une grille affichée est vide.

Gestion des erreurs

L'application devrait tourner mais parfois des problèmes arrivent. Les bases de données, même les bases incorporées, peuvent planter (i.e. quand le serveur de base de données plante, le disque est plein, ou juste provoqué par un bug), laissant l'application en suspens.

L'accès à une base de données (tout processus externe, en vrai) devrait donc toujours être intégré dans une construction try ... except et/ou try ... finally. Cela garantit que les erreurs de bases de données sont traitées et que l'utilisateur n'est pas laissé à l'abandon. Une routine rudimentaire pour notre exemple peut ressembler à ceci :

begin
  try
    SQLQuery1.Close;
    ...
    SQLQuery1.Open;
  except
    //We could use EDatabaseError which is a general database error, but we're dealing with Firebird/Interbase, so:
    on E: EDatabaseError do
    begin
      MessageDlg('Error','A database error has occurred. Technical error message: ' + E.Message,mtError,[mbOK],0);
      Edit1.Text:='';
    end;
  end;
end;

SQLite, PostgreSQL, autres bases de données

Vous pouvez soit utiliser la plus générique EDatabaseError, ou, si disponible, votre propre dérivé de EDatabaseError, si vous avez besoin de plus de détail. P.ex. SQLite et le pilote PostgreSQL dans FPC 2.6.1 et antérieur n'ont pas de E*DatabaseError spécialisé; vous devez utiliser EDatabaseError. PostgreSQL sur FPC du tronc (version de développement) a un dérivé EPQDatabaseError.

Edition des données utilisant la grille

Edition

Jusqu'à maintenant, si vous essayiez d'éditer des données dans la grille, les modifications ne pourront pas être enregistrés. Ceci parce que le SQLQuery1 n'est pas construit pour envoyer les changements vers la transaction de base de données au bon moment.

Nous avons besion de corriger cela et donc de faire le commit de la base de données, ainsi tous les changements seront écrit. Pour ceci, vous devrez utiliser du code comme ceci :

SQLQuery1.ApplyUpdates; //Pass user-generated changes back to database...
SQLTransaction1.Commit; //... and commit them using the transaction.
//SQLTransaction1.Active now is false

Nous voulons garantir que toutes les éditions (insertions, mises à jour, suppressions) sont écrites dans la base de données :

  • quand l'utilisateur change le critère de filtrage et presse le bouton poutr interroger la base de données.
  • quand la fiche est fermée.

Il est pertinent de faire une procédure séparée pour ceci qui est appelée pour ces deux instances. Allez au code et ajoutez une ligne vide ici :

  TForm1 = class(TForm)
    Button1: TButton;
    Datasource1: TDatasource;
    DBGrid1: TDBGrid;
    Edit1: TEdit;
    DBConnection: TIBConnection;
    SQLQuery1: TSQLQuery;
    SQLTransaction1: TSQLTransaction;
*****insert the empty line here****
    procedure Button1click(Sender: TObject);
    procedure Formclose(Sender: TObject; var Closeaction: Tcloseaction);
  private

puis tapez :

    procedure SaveChanges;

pressez shift-ctrl-c (combinaison par défaut) pour laisser la complétion de code créer le corps de la procédure correspondante.

Nous avons besoin d'ajouter la gestion d'erreur et contrôler que la transaction est active - souvenez-vous, ce code est aussi appelé quand le bouton est actionné la première fois, quand la transaction n'est pas encore active. Nous obtenons :

procedure Tform1.SaveChanges;
// Saves edits done by user, if any.
begin
  try
    if SQLTransaction1.Active then
    // Only if we are within a started transaction;
    // otherwise you get "Operation cannot be performed on an inactive dataset"
    begin
      SQLQuery1.ApplyUpdates; //Pass user-generated changes back to database...
      SQLTransaction1.Commit; //... and commit them using the transaction.
      //SQLTransaction1.Active now is false
    end;
  except
  on E: EDatabaseError do
    begin
      MessageDlg('Error', 'A database error has occurred. Technical error message: ' +
        E.Message, mtError, [mbOK], 0);
      Edit1.Text := '';
    end;
  end;
end;

Maintenant nous avons besoin d'appeler cette procédure aux moments appropriés :

procedure Tform1.Button1click(Sender: TObject);
begin
  SaveChanges; //Saves changes and commits transaction
  try
    SQLQuery1.Close;
....

et

procedure Tform1.Formclose(Sender: TObject; var Closeaction: Tcloseaction);
begin
  SaveChanges; //Saves changes and commits transaction
  SQLQuery1.Close;
....

Maintenant testez et voyez si les modifications réalisées dans la grille sont enregistrées dans la base de données.

Masquage de la colonne de clé primaire

Souvent, vous ne voulez pas que vos utilisateurs voient les clés primaires auto-incrémentées car elles ne servent qu'à maintenir l'intégrité référentielle. Si les utilisateurs les voient, ils voudront peut-être essayer de modifier les numéros, se fâcher lorsque les chiffres changent, qu'il y ait des lacunes dans les chiffres, etc.

Dans notre exemple, CUST_NO est la clé primaire, avec du contenu auto-généré par Firebird en utilisant des triggers et un générateur (séquence). ,Ceci veut dire que vous pouvez insérer un nouvel enregistrement sans spécifier le champ CUST_NO ; Firebird créera la valeur du champ automatiquement.

Nous pouvons changer simplement notre propriété SQLQuery1.SQL.Text pour ne pas inclure CUST_NO, mais cela amènera à des problèmes lors de l'édition des données - la clé primaire est demandée pour identifier l'enregistement/ ligne en question.

Donc utilisons une astuce pour récupérer toutes les colonnes/champs mais empêcher la grille d'afficher le premier champ, CUST_NO : dans la procédure Button1Click, ajoutez le code comme cela :

procedure Tform1.Button1click(Sender: TObject);
begin
...
    SQLQuery1.Open;
    // Hide the primary key column which is the first column in our queries.
    // We can only do this once the DBGrid has created the columns
    DBGrid1.Columns[0].Visible:=false;

Recompilez et contrôlez pour voir si la colonne de clé primaire est effectivement cachée.

SQLite, autres bases de données

  • Autres base de données : de nombreuses autres bases de données utilisent un type de champ 'autonumber' ou 'autoinc' pour fournir du contenu de champ auto-généré. Essayez de changer votre définition de table pour voir si cela fonctionne.
  • Sqlite : l'exmple du dessus fonctionne pour SQLite car nous utilisons une clé primaire entière. Voir la the documentation pour des détails.

Insertion de nouvelles données

Si vous insérez de nouveaux enregistements/lignes sans aucune information CUST_NO, vous pouvez avoir remarqué que vous obtenez le message d'erreur : Field CUST_NO is required, but not supplied. Ceci survient aussi si vous cachez la colonne CUST_NO comme dans la section précédente.

La raison : Lazarus pense que CUST_NO est requis. Cela n'est pas étrange, car c'est une clé primaire et la définition de table sous-jacente dans la base de données dit qu'il est requis.

Si nous pouvons intimer à Lazarus que ce champ n'est pas actuellement requis, nous pouvons passer des valeurs vides (=valeurs à NULL) à la base de données. Heureusement, un objet champ de requête a une propriété Required qui sert exactement à cela.

Modifiez le code en quelque chose comme :

    SQLQuery1.Open;
    {
    Make sure we don't get problems with inserting blank (=NULL) CUST_NO values, e.g.:
    Field CUST_NO is required, but not supplied
    We need to tell Lazarus that, while CUST_NO is a primary key, it is not required
    when inserting new records.
    }
    SQLQuery1.FieldByName('CUST_NO').Required:=false;
    // Hide the primary key column which is the first column in our queries.
    // We can only do this once the DBGrid has created the columns
    DBGrid1.Columns[0].Visible:=false;

Suppression de données

Vous pouvez laisser les utilisateurs employer la souris pour cela. Vous n'avez même pas besoin de code une seule ligne pour cette fonctionnalité.

Sur l'onglet 'Data controls', sélectionner le composant TDBNavigator et posez-le sur la fiche, au dessus de la grille.

Pour indiquer à quoi est relié le navigateur, mettez votre composant DataSource (DataSource1) dans sa propriété DataSource en utilisant l'inspecteur d'objet.

Maintenant vous pouvez utiliser le bouton sur le DBNavigator pour supprimer des enregistrements, mais aussi pour en insérer et vous déplacer parmi les enregistrements. Aussi, en éditant les cellules/champs, vous pouvez utiliser le bouton Cancel pour annuler vos éditions.

Pour permettre à l'utilisateur de supprimer la ligne sur laquelle ils sont dans la grille en utilisant la touche Delete, ajoutez l'unité LCLType (elle contient les définitions des codes de touche) à votre clause uses :

uses
  Classes, SysUtils, sqldb, pqconnection, DB, FileUtil, Forms,
  Controls, Graphics, Dialogs, DBGrids, StdCtrls, DbCtrls, LCLType;

... il faut coder l'événement KeyUppour la grille, qui survient quand une touche est relâchée dans la grille. Toutefois; nous avons besoin de contrôler que l'utilisateur n'est pas en train de modifier un champ - comme il peut utiliser la touche Delete pour supprimer des lettres plutôt que l'enregistrement sur lequel il travaille.

Sélectionnez la grille et allez sur les événements pour créer l'événement OnKeyUp comme ceci :

procedure TForm1.DBGrid1KeyUp(Sender: TObject; var Key: Word; Shift: TShiftState
  );
begin
  // Check for del key being hit and delete the current record in response
  // as long as we're not editing data
  if (key=VK_DELETE) and (not(DBGrid1.EditorMode)) then
  begin
    //... delete current record and apply updates to db:
    SQLQuery1.Delete;
    SQLQuery1.ApplyUpdates;
  end;
end;
Light bulb  Remarque: Par défaut, dans la propriété Options du TDBGrid, dgDisableDelete est mise à False, ce qui veut dire que l'utilisateur peut supprimer un enregistrement avec la combinaison ctrl+delete. Vous pouvez ne pas désirer ce comportement.

Résumé

Si vous avez tout suivi jusqu'à maintenant, vous pouvez récupérer des données de la base, les filtrer, les modifier, les supprimer dans la grille. Votre code ressemble à quelque chose comme cela.

unit sqldbtutorial1unit;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, sqldb, pqconnection, DB, FileUtil, Forms,
  Controls, Graphics, Dialogs, DBGrids, StdCtrls, DbCtrls, LCLType;
type

  { TForm1 }

  TForm1 = class(TForm)
    Button1: TButton;
    DatabaseName: TEdit;
    Datasource1: TDatasource;
    DBGrid1: TDBGrid;
    Dbnavigator1: Tdbnavigator;
    Edit1: TEdit;
    Label2: Tlabel;
    Label3: Tlabel;
    Label4: Tlabel;
    Label5: Tlabel;
    Password: TEdit;
    UserName: TEdit;
    ServerName: TEdit;
    DBConnection: TIBConnection;
    Label1: TLabel;
    SQLQuery1: TSQLQuery;
    SQLTransaction1: TSQLTransaction;
    procedure SaveChanges;
    procedure Button1click(Sender: TObject);
    procedure Formclose(Sender: TObject; var Closeaction: Tcloseaction);
  private
    { private declarations }
  public
    { public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.lfm}

{ TForm1 }

procedure Tform1.Savechanges;
// Saves edits done by user, if any.
begin
  try
    if SQLTransaction1.Active then
    // Only if we are within a started transaction
    // otherwise you get "Operation cannot be performed on an inactive dataset"
    begin
      SQLQuery1.ApplyUpdates; //Pass user-generated changes back to database...
      SQLTransaction1.Commit; //... and commit them using the transaction.
      //SQLTransaction1.Active now is false
    end;
  except
  on E: EDatabaseError do
    begin
      MessageDlg('Error', 'A database error has occurred. Technical error message: ' +
        E.Message, mtError, [mbOK], 0);
      Edit1.Text := '';
    end;
  end;
end;

procedure TForm1.DBGrid1KeyUp(Sender: TObject; var Key: Word; Shift: TShiftState
  );
begin
  // Check for del key being hit and delete the current record in response
  // as long as we're not editing data
  if (key=VK_DELETE) and (not(DBGrid1.EditorMode)) then
  begin
    //... delete current record and apply updates to db:
    SQLQuery1.Delete;
    SQLQuery1.ApplyUpdates;
  end;
end; 

procedure Tform1.Button1click(Sender: TObject);
begin
  SaveChanges; //Saves changes and commits transaction
  try
    SQLQuery1.Close;
    //Connection settings for Firebird/Interbase database
    //only needed when we have not yet connected:
    if not DBConnection.Connected then
    begin
      DBConnection.HostName := ServerName.Text;
      DBConnection.DatabaseName := DatabaseName.Text;
      DBConnection.Username := UserName.Text;
      DBConnection.Password := Password.Text;
      // Now we've set up our connection, visually show that
      // changes are not possibly any more
      ServerName.ReadOnly:=true;
      DatabaseName.ReadOnly:=true;
      UserName.ReadOnly:=true;
      Password.ReadOnly:=true;
    end;
    // Show all records, or filter if user specified a filter criterium
    if Edit1.Text='' then
      SQLQuery1.SQL.Text := 'select * from CUSTOMER'
    else
    begin
      SQLQuery1.SQL.Text := 'select * from CUSTOMER where COUNTRY = :COUNTRY';
      SQLQuery1.Params.ParamByName('COUNTRY').AsString := Edit1.Text;
    end;
    DBConnection.Connected := True;
    SQLTransaction1.Active := True; //Starts a new transaction
    SQLQuery1.Open;
    {
    Make sure we don't get problems with inserting blank (=NULL) CUST_NO values, i.e. error message:
    "Field CUST_NO is required, but not supplied"
    We need to tell Lazarus that, while CUST_NO is a primary key, it is not required
    when inserting new records.
    }
    SQLQuery1.FieldByName('CUST_NO').Required:=false;
    {
    Hide the primary key column which is the first column in our queries.
    We can only do this once the DBGrid has created the columns
    }
    DBGrid1.Columns[0].Visible:=false;
  except
    // EDatabaseError is a general error; 
    // you could also use one for your specific db, e.g.
    // use EIBDatabaseError for Firebird/Interbase
    on E: EDatabaseError do
    begin
      MessageDlg('Error', 'A database error has occurred. Technical error message: ' +
        E.Message, mtError, [mbOK], 0);
      Edit1.Text := '';
    end;
  end;
end;

procedure Tform1.Formclose(Sender: TObject; var Closeaction: Tcloseaction);
begin
  SaveChanges; //Saves changes and commits transaction
  SQLQuery1.Close;
  SQLTransaction1.Active := False;
  DBConnection.Connected := False;
end;

end.

Base de données incorporée sans modification de code

Firebird sur Windows

Un bonus pour les utilisateurs de Firebird sur Windows ; si vous avez suivi ce tutoriel (même si vous n'avez fait que l'exemple de base), vous avez renommé la bibliothèque incorporée de Firebird fbembed.dll en fbclient.dll. avec ceci, Lazarus se connectera aux serveurs Firebird normaux (sur une machine distante ou sur votre machine locale). Toutefois, vous pouvez aussi copier la base de données employee.fdb dans votre dossier d'application, exécuter l'application, effacer le TEdit Server name et utiliser Firebird incorporé pour vous connecter directement au fichier de la base de données, sans configuration de serveur.

Ceci est génial si vous souhaitez déployer des applications de base de données vers les utilisateurs finaux, mais ne souhaitez pas avoir du mal à installer des serveurs (vérifier si un serveur est déjà installé, s'il s'agit de la bonne version, faire en sorte que les utilisateurs vérifient les pare-feu, etc.).

Voir Firebird incorporé pour plus de détails.

Note (non vérifiée) : Depuis 2011, FPC tente de charger d'abord fbembed.dll, donc vous n'avez pas besoin de renommer fbclient.dll pour que cela fonctionne.

Firebird sur Linux/OSX/Unix

Il doit y avoir un moyen pour que cela fonctionne sur Linux / OSX. Voir Firebird pour les conseils et les liens. Les mises à jour du wiki sont les bienvenues.

SQLite

Certes, SQLite offre une fonctionnalité incorporée mais d'un autre côté, il ne permet pas de configuration client/serveur. En suivant le tutoriel du haut, vous pouvez voir cela qu'en basculant les bases de données (p.ex. SQLite et Firebird) ne fait pas tant de travail que cela.

Autres bases de données

Votre base de données peut offrir des fonctionnalités similaires. Des mises à jour de ce Wiki pour d'autres SGBD sont les bienvenues.

Voir aussi