Gestion d'une base de données MySQL

Avec les composants natifs de Lazarus


précédentsommairesuivant

IV. Exemple 2 : édition d'une table au choix

Notre premier exemple nous a permis de nous familiariser avec les composants nécessaires à la communication avec une base de données MySQL.

Nous allons créer un second projet un peu plus élaboré, qui va nous permettre de sélectionner une des tables, de l'afficher dans un DBGrid et d'en modifier le contenu.

Créez un nouveau projet de type Application.

Dans l'inspecteur d'objets, renommez la fiche principale (Form1 par défaut) en MainForm. Agrandissez la taille de la fiche. Déposez-y, tout comme dans le premier exemple, les composants invisibles qui permettent de communiquer avec la base de données :

  • un connecteur de la bonne version, que vous renommez directement en MySQLConnection ;
  • un TSQLTransaction ;
  • un TSQLQuery.

Cette fois, les propriétés HostName, DatabaseName, UserName et Password de MySQLConnection restent vierges de toute valeur initiale ; la propriété Transaction doit être initialisée à SQLTransaction1 et la propriété CharSet doit être initialisée à UTF8. La propriété Database de SQLQuery1 doit être initialisée à MySQLConnection.

IV-A. Mot de passe de connexion

Nous allons faire en sorte que l'utilisateur du programme tape, au début du programme, le mot de passe permettant de se connecter à la base de données. Nous allons faire cela au moment où la fenêtre de l'application reçoit le focus.

Cliquez, dans l'inspecteur d'objets, sur la fiche elle-même, et allez dans l'onglet Événements. Cliquez sur les trois points en regard de l'événement OnActivate, ce qui aura pour effet de créer dans le code source la méthode événementielle FormActivate.

Voici le contenu de cette méthode :

 
Sélectionnez
procedure TMainForm.FormActivate(Sender: TObject);
var
  LPassword : String;
begin
  MySQLConnection.HostName := '192.168.0.1';
  MySQLConnection.DatabaseName := 'location';
  MySQLConnection.UserName := 'mysqldvp';
  if InputQuery('Connexion à la base de données', 'Tapez votre mot de passe :', True, LPassword)
     then
       begin
         MySQLConnection.Password := LPassword;
         try
           MySQLConnection.Connected := True;
           SQLTransaction1.Active := True;
         except
           on e: EDatabaseError do
             begin
               MessageDlg('Erreur de connexion à la base de données.'#10#13'Le mot de passe est peut-être incorrect ?'#10#10#13'Fin de programme.', mtError, [mbOk], 0);
               Close;
             end;
         end;
       end
     else   (* Pas de mot de passe : fin de programme *)
       Close;
end;

Ajoutez également l'unité db à la clause uses (pour EDatabaseError).

Détaillons tout cela. Pour commencer, nous initialisons les propriétés HostName, DatabaseName et UserName du connecteur. Le mot de passe est demandé au moyen d'un dialogue InputQuery. Si l'utilisateur annule l'entrée du mot de passe, le programme se ferme sans autre forme de procès ; s'il entre un mot de passe erroné, l'exception déclenchée par le connecteur est interceptée dans le bloc try...except, un message d'erreur est affiché et l'application est fermée.

Avant de réaliser nos premiers tests à ce stade, n'oublions pas de faire en sorte que la connexion à la base de données soit proprement coupée à la fermeture du programme. Comme dans le premier exemple, créez une méthode événementielle pour l'événement OnClose :

 
Sélectionnez
procedure TMainForm.FormClose(Sender: TObject; var CloseAction: TCloseAction);
begin
  SQLQuery1.Close;
  if SQLTransaction1.Active
     then
       SQLTransaction1.Active := False;
  if MySQLConnection.Connected
     then
       MySQLConnection.Connected := False;
end;

Enregistrez le projet, en l'appelant mysql02 et en l'enregistrant dans un nouveau répertoire du même nom.

Compilez le programme et exécutez-le depuis l'EDI Lazarus. Vous constatez qu'une fenêtre vierge apparaît et qu'immédiatement le dialogue InputQuery vous demande le mot de passe :

Image non disponible

Si vous tapez le mot de passe correct (« passmysqldvp », rappelez-vous), la fenêtre de l'application (qui ne contient rien de visible pour l'instant) reste ouverte ; si vous cliquez sur Cancel, l'application se ferme et si vous tapez exprès un mot de passe erroné, un message d'erreur apparaît :

Image non disponible

Tiens, mais ce n'est pas le message d'erreur que nous avons prévu dans la méthode FormActivate ? En effet, mais si vous cliquez sur le bouton Continuer, celui que nous avions prévu apparaît bien :

Image non disponible

Pourquoi ? Tout simplement parce que le premier message d'erreur est renvoyé par le débogueur, qui lève le premier l'exception.

Plutôt que d'exécuter le programme depuis l'EDI, utilisez l'exécutable qui a été créé dans le répertoire mysql02.

Exécutez-le et faites l'expérience d'entrer un mot de passe erroné : c'est bien le message d'erreur que nous avons prévu qui s'affiche, puisque l'exécution du programme n'est plus encadrée par le débogueur.

IV-A-1. Affichage de l'erreur renvoyée par le système

Notre message d'erreur n'est pas très précis, car il apparaît identiquement si le mot de passe est erroné et si, par exemple, le serveur est inaccessible ou la base de données inexistante.

Pour pouvoir identifier plus précisément l'erreur qui s'est produite, il faut lever une exception descendante de EDatabaseError : ESQLDatabaseError. Dans les propriétés de ce type d'exception se trouvent un code et des messages propres à MySQL.

Une liste des erreurs possibles est consultable dans la référence de MySQL en ligne.

Voici une version plus élaborée de notre méthode de login :

 
Sélectionnez
procedure TMainForm.FormActivate(Sender: TObject);
(* Lecture du mot de passe et connexion à la base de données *)
var
  LPassword : String;
begin
  (* Données de la connexion *)
  MySQLConnection.HostName := '192.168.0.1';
  MySQLConnection.DatabaseName := 'location';
  MySQLConnection.UserName := 'mysqldvp';
  (* Lecture du mot de passe *)
  if InputQuery('Connexion à la base de données', 'Tapez votre mot de passe :', True, LPassword)
     then
       begin
         (* Connexion à la base de données *)
         MySQLConnection.Password := LPassword;
         try
           MySQLConnection.Connected := True;
           SQLTransaction1.Active := True;
         except
           on e: ESQLDatabaseError do
             begin   (* Erreur renvoyée par MySQL : fin de programme *)
               MessageDlg('Erreur de connexion à la base de données :'#10#10#13 +
                          IntToStr(e.ErrorCode) + ' : ' + e.Message +
                          #10#10#13'Fin de programme.', mtError, [mbOk], 0);
               Close;
             end;
           on e: EDatabaseError do
             begin   (* Erreur de connexion : fin de programme *)
               MessageDlg('Erreur de connexion à la base de données.'#10#10#13'Fin de programme.', mtError, [mbOk], 0);
               Close;
             end;
         end;
       end
     else   (* Pas de mot de passe : fin de programme *)
       Close;
end;

Par exemple, si la base de données n'existe pas :

Image non disponible

La gestion de l'exception EDatabaseError suit celle de ESQLDatabaseError. Le principe général est de gérer en cascade les exceptions de la plus spécialisée à la moins spécialisée : ainsi, si la connexion au serveur MySQL est possible, les erreurs MySQL seront traitées en priorité (ESQLDatabaseError).

Vous avez certainement remarqué que les messages d'erreur sont en anglais, la langue par défaut de Lazarus. Pour obtenir les messages en français, je vous renvoie au tutoriel de Gilles Vasseur sur l'internationalisation d'une application.

IV-B. Récupération de la liste des tables

À présent, nous allons faire en sorte que l'utilisateur puisse choisir une des tables contenues dans la base de données pour en afficher le contenu.

Sélectionnez un composant TListBox dans l'onglet Standard et déposez-le en bas et à gauche sur la fiche du projet. Dans l'inspecteur d'objets, renommez sa propriété Name en lbTables.

Dans l'éditeur de source, allez dans la déclaration du type TMainForm (que Lazarus a automatiquement déclaré ainsi lorsque vous avez appelé MainForm votre fiche principale). Dans la section private, ajoutez cette procédure :

 
Sélectionnez
procedure ShowTables;

Pressez la combinaison de touches Shift-Control-C pour que Lazarus crée la procédure dans la section implementation et complétez cette dernière :

 
Sélectionnez
procedure TMainForm.ShowTables;
begin
  SQLQuery1.Close;
  SQLQuery1.SQL.Text := 'SHOW TABLES;';
  SQLQuery1.Open;
  while not SQLQuery1.EOF do
    begin
      lbTables.Items.Add(SQLQuery1.Fields[0].AsString);
      SQLQuery1.Next;
    end;
  (* Sélection du premier élément *)
  if SQLQuery1.RecordCount > 0
     then
       lbTables.ItemIndex := 0;
end;

Tout d'abord, la commande SQL qui permet de retourner la liste des tables d'une base de données est SHOW TABLES;.

Cette commande est passée au composant SQLQuery1 dans sa propriété SQL.Text (comme nous avions fait dans notre premier exemple) et est exécutée dans la méthode Open. Ensuite, nous bouclons pour que chaque nom de table retourné par la requête soit ajouté dans la listbox lbTables.

Placez l'appel de cette méthode privée ShowTables dans le bloc try de la méthode événementielle FormActivate de la fiche principale :

 
Sélectionnez
procedure TMainForm.FormActivate(Sender: TObject);
var
  LPassword : String;
begin
  { . . . }
  if InputQuery('Connexion à la base de données', 'Tapez votre mot de passe :', True, LPassword)
     then
       begin
         MySQLConnection.Password := LPassword;
         try
           MySQLConnection.Connected := True;
           SQLTransaction1.Active := True;
           ShowTables;   // ***** AJOUT *****
         except
           { . . . }
         end;
       end
     else   (* Pas de mot de passe : fin de programme *)
       Close;
end;

Vous pouvez faire un test d'exécution à ce stade, pour vérifier que la liste des tables est bien chargée dans la listbox :

Image non disponible

IV-C. Affichage du contenu de la table sélectionnée

Comme dans le premier exemple, nous allons afficher le contenu d'une table dans un composant TDBGrid, de l'onglet Data Controls, que vous déposez en haut et à gauche de votre fiche principale, et dont vous agrandissez la largeur jusqu'à la limite droite de la fiche et la hauteur jusqu'à un peu au-dessus de la listbox.

L'utilisation du DBGrid nous conduit à ajouter également un composant TDataSource, de l'onglet Data Access :

Image non disponible

Toujours comme dans l'exemple 1, la propriété DataSet du TDataSource doit être affectée à SQLQuery1, et la propriété DataSource du TDBGrid à DataSource1.

Dans l'inspecteur d'objets, sélectionnez la listbox lbTables, allez dans l'onglet Événements, trouvez l'événement OnSelectionChange et cliquez sur les trois points en regard de celui-ci pour créer une méthode événementielle qui s'exécutera à chaque changement de choix de table :

 
Sélectionnez
procedure TMainForm.lbTablesSelectionChange(Sender: TObject; User: boolean);
begin
  SQLQuery1.Close;
  SQLQuery1.SQL.Text := 'SELECT * FROM ' + lbTables.GetSelectedText + ';';
  SQLQuery1.Open;
end;

Simplement, la requête SELECT * FROM est complétée par le nom de la table sélectionnée dans la listbox.

Testez l'application en l'état :

Image non disponible

Le fait de sélectionner une autre table charge automatiquement son contenu dans le DBGrid.

Si vous êtes très attentif(ve), vous verrez très fugacement apparaître la liste des tables dans le DBGrid, juste avant que le contenu de la première table s'affiche. C'est dû au fait que le DBGrid, via le DataSource, affiche le contenu du dataset du SQLQuery. Pour éviter cette brève apparition, il aurait fallu ne pas lier le DBGrid au DataSource dans ses propriétés et ajouter cette instruction juste après l'exécution de ShowTables (dans la méthode FormActivate) : DBGrid1.DataSource := DataSource1;.

IV-D. Permettre de modifier les données

Nous avons laissé un peu de place entre le bord supérieur de la listbox et le bord inférieur du DBGrid pour pouvoir y insérer une barre d'outils de navigation : un composant TDBNavigator (que l'on trouve au début de l'onglet Data Controls). Centrez-le ou donnez-lui la même largeur que le DBGrid :

Image non disponible

Affectez DataSource1 à sa propriété DataSource. Dans l'inspecteur d'objets, vous pouvez choisir quels boutons vous voulez afficher dans le DBNavigator, dans sa propriété VisibleButtons. Décochez notamment le bouton nbRefresh, qui n'a aucune utilité dans notre exemple.

Exécutez à nouveau l'application et expérimentez la navigation avec les boutons verts, mais aussi l'insertion de lignes, la suppression, etc. Vous pouvez y aller franchement et tout casser : les modifications que vous faites affectent juste le contenu du DBGrid, mais pas la base de données elle-même. D'ailleurs, si vous changez votre choix de table dans la listbox, vous voyez qu'à chaque réaffichage la table est restaurée dans son état d'origine.

La dernière étape va être d'enregistrer les modifications dans la base de données.

Ne martyrisez pas trop la base de données dans les tests que vous ferez dorénavant, car nous en aurons encore besoin pour la suite du tutoriel.

Nous allons donc écrire une méthode privée qui va s'occuper de mettre à jour les données des tables dans la base. Cette méthode sera appelée à chaque fois que l'on changera de table dans la listbox, ainsi qu'à la fermeture de l'application.

Retournez dans l'éditeur de source et ajoutez cette méthode dans la section private de TMainForm :

 
Sélectionnez
procedure CommitChanges;

Un petit Shift-Control-C pour son implémentation :

 
Sélectionnez
procedure TmainForm.CommitChanges;
begin
  if SQLTransaction1.Active
     then
       try
         SQLQuery1.ApplyUpdates;
         SQLTransaction1.Commit;
       except
         on e: EDatabaseError do
           begin
             MessageDlg('Erreur d''enregistrement des modifications', mtError, [mbOk], 0);
           end;
       end;
end;

Appelons-la dans la méthode événementielle qui répond au changement de table dans la listbox :

 
Sélectionnez
procedure TMainForm.lbTablesSelectionChange(Sender: TObject; User: boolean);
begin
  CommitChanges;   // ***** AJOUT *****
  SQLQuery1.Close;
  SQLQuery1.SQL.Text := 'SELECT * FROM ' + lbTables.GetSelectedText + ';';
  SQLQuery1.Open;
end;

Et dans la méthode qui répond à la fermeture de la fiche principale :

 
Sélectionnez
procedure TMainForm.FormClose(Sender: TObject; var CloseAction: TCloseAction);
begin
  CommitChanges;   // ***** AJOUT *****
  SQLQuery1.Close;
  if SQLTransaction1.Active
     then
       SQLTransaction1.Active := False;
  if MySQLConnection.Connected
     then
       MySQLConnection.Connected := False;
end;

Testez l'application ainsi modifiée (encore une fois, en y allant mollo pour garder utilisable la base de données). Inspectez le contenu de la base avec phpMyAdmin : vous constaterez que toutes les modifications y ont bien été répercutées.

IV-E. Code complet de l'exemple 2

 
Sélectionnez
unit Main;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, mysql56conn, sqldb, FileUtil, Forms, Controls, Graphics,
  Dialogs, StdCtrls, Grids, DBGrids, DbCtrls, db;

type

  { TMainForm }

  TMainForm = class(TForm)
    DataSource1: TDataSource;
    DBGrid1: TDBGrid;
    DBNavigator1: TDBNavigator;
    lbTables: TListBox;
    MySQLConnection: TMySQL56Connection;
    SQLQuery1: TSQLQuery;
    SQLTransaction1: TSQLTransaction;
    procedure FormActivate(Sender: TObject);
    procedure FormClose(Sender: TObject; var CloseAction: TCloseAction);
    procedure lbTablesSelectionChange(Sender: TObject; User: boolean);
  private
    { private declarations }
    procedure ShowTables;
    procedure CommitChanges;
  public
    { public declarations }
  end;

var
  MainForm: TMainForm;

implementation

{$R *.lfm}

{ TMainForm }

procedure TMainForm.FormClose(Sender: TObject; var CloseAction: TCloseAction);
(* Fermeture propre de la connexion avant la fin de programme *)
begin
  (* Enregistrement des éventuelles modifications *)
  CommitChanges;
  (* Fermeture de la connexion *)
  SQLQuery1.Close;
  if SQLTransaction1.Active
     then
       SQLTransaction1.Active := False;
  if MySQLConnection.Connected
     then
       MySQLConnection.Connected := False;
end;

procedure TMainForm.lbTablesSelectionChange(Sender: TObject; User: boolean);
(* Sélection d'une table dans la listbox *)
begin
  (* Enregistrement des éventuelles modifications *)
  CommitChanges;
  (* Chargement des données de la table choisie *)
  SQLQuery1.Close;
  SQLQuery1.SQL.Text := 'SELECT * FROM ' + lbTables.GetSelectedText + ';';
  SQLQuery1.Open;
end;

procedure TMainForm.ShowTables;
(* Chargement de la liste des tables dans la listbox *)
begin
  SQLQuery1.Close;
  SQLQuery1.SQL.Text := 'SHOW TABLES;';
  SQLQuery1.Open;
  while not SQLQuery1.EOF do
    begin
      lbTables.Items.Add(SQLQuery1.Fields[0].AsString);
      SQLQuery1.Next;
    end;
  if SQLQuery1.RecordCount > 0
     then
       lbTables.ItemIndex := 0;
end;

procedure TMainForm.CommitChanges;
(* Enregistrement des modifications *)
begin
  if SQLTransaction1.Active
     then
       try
         SQLQuery1.ApplyUpdates;
         SQLTransaction1.Commit;
       except
         on e: EDatabaseError do
           begin
             MessageDlg('Erreur d''enregistrement des modifications', mtError, [mbOk], 0);
           end;
       end;
end;
procedure TMainForm.FormActivate(Sender: TObject);
(* Lecture du mot de passe et connexion à la base de données *)
var
  LPassword : String;
begin
  (* Données de la connexion *)
  MySQLConnection.HostName := '192.168.0.1';
  MySQLConnection.DatabaseName := 'location';
  MySQLConnection.UserName := 'mysqldvp';
  (* Lecture du mot de passe *)
  if InputQuery('Connexion à la base de données', 'Tapez votre mot de passe :', True, LPassword)
     then
       begin
         (* Connexion à la base de données *)
         MySQLConnection.Password := LPassword;
         try
           MySQLConnection.Connected := True;
           SQLTransaction1.Active := True;
           ShowTables;
         except
           on e: EDatabaseError do
             begin   (* Erreur de connexion : fin de programme *)
               MessageDlg('Erreur de connexion à la base de données.'#10#13'Le mot de passe est peut-être incorrect ?'#10#10#13'Fin de programme.', mtError, [mbOk], 0);
               Close;
             end;
         end;
       end
     else   (* Pas de mot de passe : fin de programme *)
       Close;
end;
end.

Téléchargez le projet mysql02 ici.


précédentsommairesuivant

Vous avez aimé ce tutoriel ? Alors partagez-le en cliquant sur les boutons suivants : Viadeo Twitter Facebook Share on Google+   

  

Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright © 2017 Alcatîz. Aucune reproduction, même partielle, ne peut être faite de ce site ni de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.