menu
  Home  ==>  articles  ==>  bdd  ==>  oracle  ==>  oracle_tutorial   

Tutorial ORACLE - John COLIBRI.


1 - Introduction

Nous allons présenter ici comment gérer une application Client Oracle en utilisant les composants Delphi. Après la lecture de cet article, vous saurez:
  • créer une Table et ses indexes
  • lire, écrire et modifier les données de cette table
  • travailler en mode nomade (non connecté au Serveur)
  • gérer plusieurs tables liées



2 - Vérification de l'Accès au Serveur

Dans les articles précédents, nous avons présenté:

Nous utiliserons les paramètres par défaut Oracle classiques:
  • nom d'utilisateur: SCOTT
  • mot de passe: TIGER
Si votre administrateur vous a fourni d'autres noms d'utilisateur / mot de passe, vous remplacerez SCOTT/TIGER par vos valeurs.



Si vous utilisez une base déjà installée, voici comment vérifier que vous arrivez bien à communiquer avec votre Serveur Oracle:

  • si vous travaillez directement sur le PC sur lequel est installé le Serveur:
       chargez l'outil graphique SqlPlus (démarrer | Programmes | OraDb10g_home1 | Application Development | Sql Plus)

    image

       Sql-Plus nous demande qui nous sommes

    image

       nous tapons SCOTT, TIGER et ORCL puis cliquons "OK"

       Sql-Plus nous accueille:

    image

       nous affichons la table EMP (ou n'importe quelle autres requête correspondant à une table)

      SELECT * FROM emp; Entrée  

       Oracle répond

    image



    Notez que "Host String" correspond au paramètre qui se trouve dans "OracleHome"\NetWork\ADMIN\tnsnames.ora:

     
    # tnsnames.ora Network Configuration File: C:\oracle\product\10.1.0\Db_1\network\admin\tnsnames.ora
    # Generated by Oracle configuration tools.

    ORCL =
      (DESCRIPTION =
        (ADDRESS = (PROTOCOL = TCP)(HOST = pol-1400)(PORT = 1521))
        (CONNECT_DATA =
          (SERVER = DEDICATED)
          (SERVICE_NAME = orcl)
        )
     )

  • si vous souhaitez placer votre application sur le PC contenant le Client Oracle (ce qui est plus classique, mais oblige à utiliser 2 PC), depuis le PC contenant le Client:
       sélectionnez "démarrer | programmes | OraClient10g_home1 | application Developpments | SQL Plus"

    image

       Sql-Plus nous demande qui nous sommes:

    image

       nous tapons SCOTT, TIGER et MY_NAME_RESOLUTION puis cliquons "OK"

       Sql-Plus nous accueille:

    image

       nous affichons la table EMP (ou n'importe quelle autres requête correspondant à une table)

      SELECT * FROM emp; Entrée  

       le Serveur renvoie le contenu de cette table


    Notez que "Host String" correspond au paramètre qui se trouve dans "OracleHome"/NetWork/ADMIN/tnsnames.ora:

     
    # tnsnames.ora Network Configuration File: C:\oracle\product\10.1.0\Client_1\NETWORK\ADMIN\tnsnames.ora
    # Generated by Oracle configuration tools.

    MY_NAME_RESOLUTION =
      (DESCRIPTION =
        (ADDRESS_LIST =
          (ADDRESS = (PROTOCOL = TCP)(HOST = pol-1400)(PORT = 1521))
        )
        (CONNECT_DATA =
          (SERVICE_NAME= ORCL)
        )
      )





3 - Architecture de l'Application Client

Le fonctionnement Client / Serveur traditionnel est organisé avec un Serveur qui répond à plusieurs Clients, chacun situé sur des PC différents:



Prenons le cas d'un Client particulier:

  • lorsque ce Client souhaite communiquer avec le Serveur, il envoie une requête via le réseau:

    image

  • et le Serveur répond, en un ou plusieurs paquets:

    image



Du côté du Client, nous avons les couches logicielles suivantes:
  • au niveau le plus bas, le PC est équipé des couches réseau. La pile TCP/IP par exemple, qui est installée automatiquement par Windows:

    image

  • cette couche est attaquée par le Client Oracle (appelée jadis SqlNet) qui a été installée lors de l'installation Client Oracle:

    image

  • le Client Oracle est attaqué par les composants d'accès Delphi, qui communiqueront avec l'application utilisateur en utilisant les contrôles Delphi:

    image

  • et finalement l'application utilisateur (comptabilité, facturation, portail web pour un catalogue client etc):

    image



Au niveau Delphi, les étapes d'une communication sont les suivantes:
  • le Client Delphi établit la connection
  • puis, pour chaque requête:
    • le Client envoie sa requête
    • le Client récupère la réponse
  • le Client ferme la connection
Ces trois étapes peuvent être réalisées par différents types de composants Delphi:
  • le BDE (Borland Database Engine)
  • les composants dbExpress
  • des composants Ado (la mécanique Microsoft)
  • des composants écrits spécialement pour gérer en Delphi des bases de données Oracle


Nous utiliserons ici les composants dbExpress fournis avec Delphi. Ces composants comportent:
  • tSqlConnection qui sert à établir la connection avec le Serveur. C'est là que nous fournissons les paramètres Oracle (SCOTT, TIGER, ORCL):

    image

  • SqlQuery qui permet d'envoyer des requêtes Sql vers le Serveur et qui reçoit les réponses. C'est à ce niveau seul que nous envoyons les requêtes SQL. Nous pouvons d'ailleurs travailler avec ce niveau seul, pour écrire des données en mode batch par exemple.

    image

  • tDatasetProvider qui transforme les réponses du Serveur en paquets au format normalisé Delphi.

    image

  • tClientDataSet qui stocke en mémoire les données reçues du Serveur et permet des mise en forme de ces données (tri, filtrage, navigation, sauvegarde locale etc):

    image

  • tDataSource et tdbGrid (ou autres tdbEdit, tdbListbox ...) qui présentent les données à l'utilisateur et lui permettent de les modifier:

    image




4 - La Connection à Oracle

Voici comment ouvrir une connection depuis le Client:
   créez une nouvelle application Delphi, et nommez-la p_oracle_dbx_connect.dpr

   dans la page dbExpress de la Palette sélectionnez SqlConnection:

image

et posez ce composant sur la Forme

   pour fournir les paramètres de login, double cliquez sur SqlConnection1

   Delphi ouvre l'éditeur de connexion:

image

   sélectionnez le pilote Oracle, et tapez les paramètres de connection (SCOTT, TIGER, ORCL):

image

Notez que ORCL est le nom qui apparaît dans votre fichier tnsnames.ora:

image

Ainsi, si nous nous étions connecté depuis le PC Client, nous aurions du utiliser MY_NAME_RESOLUTION au lieu de ORCL (cf le paragraphe 2 ci-dessus). Et vous aurez à utiliser votre nom figurant dans votre fichier tnsnames.ora.

   fermez l'éditeur de connection
   pour éviter d'avoir à taper sempiternellement SCOTT/TIGER, sélectionnez dans l'inspecteur d'objet LoginPrompt et basculez sa valeur sur False.

Puis établissez la connection en basculant Connected sur True

image

   au bout de quelques instants Connected bascule sur True, indiquant que la connection est effectivement établie.

Si tel n'était pas les cas, revoyez les étapes précédentes une à une.

   rebasculez Connected sur False. En effet les différents composants Delphi forceront l'ouverture de la connection lorsqu'ils en auront besoin.
Vous pouvez télécharger cette application en cliquant oracle_dbx_connect.zip.




5 - Création de Tables Oracle

5.1 - Principe

Nous allons créer une table contenant pour chaque formation:
  • un code (par exemple 8)
  • un nom (par exemple "Delphi Oracle")
  • un prix (par exemple 1.400)
Pour cela nous devons envoyer une requête en langage SQL vers le Serveur Oracle.

La syntaxe de cette requête est:

 CREATE TABLE formations
     (f_numero INTEGER, f_nom CHARACTER(11), f_jours INTEGER, f_prix NUMERIC(5, 2) )

Il suffit donc de choisir un nom de table, et le nom de chaque colonne avec son type.

Parmi les types autorisés par Oracle et correspondant aux types SQL usuels citons:

  • les nombres:
    • NUMERIC(decimales, précision) pour une valeur numérique flottante, avec 38 chiffres significatifs
    • de nombreuses variantes (INTEGER, SMALLINT FLOAT, REAL  ) existent, à la fois pour des raisons de portabilité et pour améliorer le nombre de chiffres significatifs
  • les chaînes de caractères:
    • CHARACTER(taille) pour chaînes avec allocation fixe, taille maximale 2000 caractères
    • NVARCHAR2(taille) pour les chaînes avec allocation dynamique, taille maximale 32.767 caractères
  • les dates:
    • DATE et TIMESTAMP  


Pour envoyer cette requête vers le Serveur:
  • nous utilisons un tSqlConnection qui assurera la connection vers le Serveur
  • nous utilisons un tSqlQuery
    • nous le relions à tSqlConnection
    • nous plaçons la requête SQL dans sa propriété tSqlQuery.SQL (via l'Inspecteur ou en code)
    • nous appelons tSqlQuery.ExecSql


5.2 - Utilisation de SQL

La requête à envoyer au Serveur est placée dans tSqlQuery.Sql. tSqlQuery.Sql est un descendant de tStrings. Nous pouvons donc utiliser, par exemple:

          SqlQuery1.Sql.Add('CREATE TABLE formations (f_numero INTEGER, f_nom CHARACTER(11))');

Pour construire la requête:

  • nous pouvons utiliser Add pour ajouter une ligne de requête, Text pour affecter une requête, Clear pour purger tout texte antérieur, ou même LoadFromFile pour lire un fichier .txt contenant la requête:
          SqlQuery1.Sql.LoadFromFile('cree_formation.txt');

La mise en page n'a aucune importance pour le Serveur: la requête peut être répartie en plusieurs lignes:

          SqlQuery1.Sql.Add('CREATE TABLE');
          SqlQuery1.Sql.Add('  formations ');
          SqlQuery1.Sql.Add('  (f_numero INTEGER, f_nom CHARACTER(11))');
  • nous pouvons entrer la requête en utilisant l'Inspecteur d'Objet. Si nous cliquons sur Sql, Delphi affiche:

    et vous pouvez taper manuellement la requête dans la fenêtre du Memo

  • nous pouvons aussi créer la String en plusieurs étapes par toutes les primitives de String telles que la concaténation, Insert, le test par Pos ou autre. Par exemple:
          l_requete:= 'CREATE TABLE 'Edit1.Text' (';
          l_requete:= l_requeteEdit2.Text')';
          SqlQuery1.Sql.Add(l_requete);



5.3 - Comment ça Marche

Au niveau fonctionnement:
  • lorsque nous construisons la requête par SqlQuery.Sql.Add, ce texte de requête est à ce stade une tStrings en mémoire de notre PC.

    C'est une tStrings comme n'importe quelle autre. Ce qui explique qu'aucune vérification de syntaxe n'est effectuée.

  • Lorsque nous exécutons:

                 SqlQuery1.ExecSql;

    alors:

    • la requête est envoyée au Serveur via le Client
    • le Serveur traite la requête et:
      • crée la table si la requête est correcte:

      • retourne une erreur transformée par Delphi en exception en cas de problème
Notez que l'envoi de toute requête qui modifie des données du Serveur (et la création d'une nouvelle table est bien une modification) ne peut se faire que par du code (et non PAS en basculant SqlQuery1.Active sur True en mode conception)



5.4 - L'application

Pour créer notre table
   créez une nouvelle application et appelez-la "p_oracle_dbx_create_table.dpr"

   ajoutez un tSqlConnection sur la Forme (comme détaillé ci-dessus):
  • dans la page "dbExpress" de la Palette, sélectionnez SqlConnection et posez-le sur la Forme
  • double cliquez pour ouvrir l'éditeur de connection, sélectionnez ORACLE puis remplissez SCOTT/TIGER/ORCL
  • dans l'Inspecteur d'Object, basculez LoginPrompt sur False
  • vérifiez la connection en basculant Connected sur True, puis rebasculez la valeur sur False
   sélectionnez dans la page "dbExpress" de la Palette le composant SqlQuery:

image

   sélectionnez sa propriété SqlConnection et initialisez-la à

    SqlConnection1

   placez un tButton sur la Forme et créez sa méthode OnClick. Placez-y les instructions de création:

procedure TForm1.create_table_Click(SenderTObject);
  begin
    with SqlQuery1 do
    begin
      Close;

      with Sql do
      begin
        Clear;
        Add('CREATE TABLE formations');
        Add('  ('
            +'    f_numero INTEGER,');
        Add('     f_nom CHAR(23),');
        Add('     f_jours SMALLINT,');
        Add('     f_prix NUMERIC(5, 2)');
        Add('   )');
      end// with Sql

      Try
        SqlConnection1.Open;

        ExecSql;
      except
        on eException do
          begin
            display('  *** pb_create 'e.Message);
          end;
      end;
    end// with SqlQuery1
  end// create_table_Click


   compilez, exécutez, et cliquez le bouton


Vous pouvez télécharger le sources du projet "ib_dbx_create_table.zip".



5.5 - Effacer une table

Pour supprimer une Table, il faut exécuter la requête:

 DROP TABLE formations

Donc:
   placez un autre tButton sur la Forme et placez-y la requête de suppression:

procedure TForm1.drop_table_Click(SenderTObject);
  begin
    with SQLQuery1 do
    begin
      Close;

      with Sql do
      begin
        Clear;
        Add('DROP TABLE formations');

        Try
          ExecSql;
        except
          on eException do
            display('  *** pb 'e.Message);
        end;
      end// with Sql
    end// with SQLQuery1
  end// drop_table_Click


   compilez, exécutez, et cliquez le bouton


Notez que:
  • nous avons le même SqlQuery en changeant le contenu de sa propriété SQL. Nous aurions aussi bien pu utiliser des SqlQuery séparés.


5.6 - Vérifier la création

Nous pouvons vérifier que la création d'une table a été effectuée
  • soit en exécutant DROP TABLE: si la table n'existe pas, le Serveur Oracle provoquera une exception.
  • soit en exécutant SELECT * FROM formations, que nous allons présenter ci-dessous
  • soit en exécutant SELECT * FROM user_tables qui affiche toutes les tables de SCOTT/TIGER. Nous allons présenter SELECT ci-dessous


5.7 - Création de deux autres tables

Ajoutons deux autres tables à notre exemple: une table pour les lieux des formation, et une table pour les dates.

Nous pourrions utiliser des procédures similaires aux précédentes, mais un examen superficiel montre que seul le contenu de la requête SQL change. Il est donc très simple de créer une procédure do_execute_sql qui utilise cette requête comme paramètres, et que nous utiliserons pour les autres traitements:

procedure do_execute_sql(p_sqlString);
  begin
    with Form1SqlQuery1Sql do
    begin
      Close;

      Clear;
      Add(p_sql);

      Try
        SqlConnection1.Open;

        ExecSql;
      except
        on eException do
          display('  *** pb_create 'e.Message);
      end// try
    end// with SqlQuery1
  end// do_execute_sql



Nous alors créer une table des villes, comportant un numéro de ville et un nom de ville, et nous imposerons une contrainte d'unicité au numéro. En utilisant la procédure précédente, la méthode utilisée est:

procedure TForm1.create_table_villes_Click(SenderTObject);
  begin
    do_execute_sql(
         'CREATE TABLE villes'
       + '  ('
       + '    v_numero INTEGER NOT NULL PRIMARY KEY,'
       + '    v_nom CHAR(30)'
       + '  )');
  end// create_table_villes_Click



Et pour effacer cette table, nous utiliserons:

procedure TForm1.drop_villes_Click(SenderTObject);
  begin
    do_execute_sql('DROP TABLE villes');
  end// drop_villes_Click



Nous utiliserons donc la procédure do_execute_sql pour les autres instructions de création / modification / effacement de tables.



Notre première table n'était pas dotée d'une clé unique. Nous pouvons corriger la déclaration par une instruction SQL ALTER  .

ALTER TABLE formations
  ADD CONSTRAINT pk_formation PRIMARY KEY (f_numero)



La table FORMATIONS et la table VILLES étant dotées de clés primaires, nous pouvons créer une table DATES qui contient des références vers les deux tables précédentes:

CREATE TABLE dates
  (
    d_formation_ref INTEGER,
    d_ville_ref INTEGER,
    d_date DATE,
    CONSTRAINT fk_formation 
      FOREIGN KEY (d_formation_ref
      REFERENCES formations (f_numero),
    CONSTRAINT fk_ville 
      FOREIGN KEY (d_ville_ref
      REFERENCES villes (v_numero)
  )



Nous vous laissons le soin d'ajouter au projet précédent les boutons avec le code pour créer (et effacer) les deux autres tables. Le texte complet se trouve bien sûr dans oracle_dbx_create_table.zip .



Notez que:

  • le schéma correspondant aux trois tables que nous avons créé est le suivant:

  • connaissant le schéma, nous aurions pu directement créer la table formation avec sa contrainte de clé primaire (évitant ALTER  )
  • notez que lorsque nous créons la table des DATES qui référence les deux autres, celles-ci DOIVENT avoir été créées et DOIVENT avoir les champs désignés dans les contraintes FOREIGN KEY
  • de façon symétrique, si nous souhaitons effacer les tables, il faut d'abord effacer DATES, puis VILLES et FORMATIONS (si nous essayons d'effacer FORMATIONS en premier, le Serveur signale qu'il existe une référence vers cette table depuis DATES)
  • nous avons fourni dans les tutoriaux Interbase des exemples de construction de tables à partir d'un schéma fourni dans un fichier .TXT
  • nous aurions pu de même générer les instructions de création de table à partir de notre outil UML qui a été utilisé pour le schéma précédent.


Voici une image de cette petite application lorsque nous avons créé les trois tables:




6 - Ajout de Données

6.1 - Ajout simple

Ajoutons un enregistrement pour le stage

    3, Oracle Delphi

L'instruction SQL est:

 INSERT INTO formations
     (f_numero, f_nom)
     VALUES (3, 'Oracle Delphi')



L'ajout d'enregistrement va modifier les données du Serveur, donc nous utiliserons tSqlQuery.ExecSql.

De façon détaillée:
   créez une nouvelle application et nommez-la "p_oracle_dbx_insert_data"
   placez un tSqlConnection sur la Forme
  • Cliquez deux fois sur SqlConnection1, renseignez "Driver Name", "Connection Name", "DataBase", "User Name" et "Pass Word".
  • dans l'Inspecteur d'Objet, basculez LoginPrompt sur False, et vérifiez la connection en basculant SqlConnection1.Connected sur True, puis fermez la connection.
   placez un tSqlQuery sur la tForme
  • sélectionnez sa propriété SqlConnection et initialisez-la à SqlConnection1
   placez un tButton sur la Forme et créez sa méthode OnClick. Placez-y les instructions d'ajout:

procedure TForm1.insert_Click(SenderTObject);
  begin
    with SqlQuery1 do
    begin
      Close;

      with Sql do
      begin
        Clear;
        Add('INSERT INTO formations');
        Add('  (f_numero, f_nom)');
        Add('  VALUES (3, ''Oracle Delphi'')');
      end;

      Try
        ExecSql;
      except
        on eException do
          display('  *** pb_insert 'e.Message);
      end;
    end// with SqlQuery1
  end// insert_Click

   compilez, exécutez, et cliquez le bouton


Vous pouvez télécharger les sources du projet "oracle_dbx_insert_data.zip".



6.2 - Type CHARACTER

Pour spécifier les valeurs CHARACTER, Sql exige que la chaîne soit entourée de guillemets. Suivant les Serveurs, il faut utiliser un guillemet simple ou double:

 VALUES (3, 'Oracle Delphi')

ou

 VALUES (3, "Oracle Delphi")

De plus si notre valeur est nichée dans une String Pascal, il faut dédoubler les guillemets simples

SqlQuery1.Sql.Add('  VALUES (3, ''Oracle Delphi'')');

Pour simplifier cet imbroglio de guillemets, Delphi propose la méthode QuotedStr:

IbQuery1.Sql.Add('  VALUES (3, 'QuotedStr('Oracle')+ ')');



6.3 - Type NUMERIC

Pour les valeurs numériques avec décimales, nous devons batailler avec les points et les virgules:
  • aux US, la partie entière est séparée de la partie décimale par un point:

        3.1415

    alors qu'en Europe nous disons

        3,1415

  • les valeurs que nous tapons en PASCAL doivent donc respecter la syntaxe US

  • mais si nous utilisons des primitives de conversion interface utilisateur <=> Delphi, Delphi lit les paramètres de localisation dans la base de registre Windows. Pour ma machine il est dit que le séparateur décimal est la virgule. Par conséquent les fonctions telles que:

        FloatToStr

    attendent une virgule

    Nous pouvons imposer le séparateur à utiliser en spécifiant:

DecimalSeparator:= '.';

La règle est donc simple:

  • les valeurs Delphi (Double ou autre) utilisent le point '.'
  • les composants visuels (tEdit etc) et les primitives de conversion (FloatToStr) utilisent la virgule ','


6.4 - Type DATE  

Un problème similaire intervient pour les dates:
  • Sql utilise le format US "année mois jour". EXCLUSIVEMENT
  • suivant le type de primitive, Delphi utilisera un format US ou Européanisé (via le panneau de configuration Windows, ou des primitives de formatage)
  • le séparateur doit être un slash / (et non pas un tiret ou autre)

         2004/03/29

  • les dates doivent être entourée de guillemets:

         '2004/03/29'

    car sinon Delphi effectue une division et calcule un Double, assimilé à un tDateTime, et traduit en une date

En supposant que nous souhaitions fournir la date du 29 Mars 2004, nous pouvons utiliser:

 INSERT INTO dates
     (d_numero, d_date)
     VALUES (3, '2004/03/29')

et:

SqlQuery1.Sql.Add('INSERT INTO dates');
SqlQuery1.Sql.Add('  (d_numero, d_date');
SqlQuery1.Sql.Add('  VALUES (3, ''2004/03/29'')');



6.5 - Automatisation de l'Ajout

Les valeurs à insérer ont été figées dans notre code. Nous pouvons généraliser cet ajout
  • soit en mode interactif.
  • soit par une procédure amplement paramétrée
  • soit par des scripts


Nous allons présenter ici l'utilisation d'une procédure paramétrée.

En fait il n'y a rien de nouveau par rapport à la technique ci-dessus, sauf que

  • la chaîne de la requête est construite en fonction de paramètres de la procédure
  • la procédure appelante envoie les paramètres requis


Voici un exemple de procédure générique d'ajout:

procedure insert_generic(p_numberIntegerp_nameStringp_daysIntegerp_costDouble);
  begin
    with Form1SqlQuery1 do
    begin
      Close;

      with Sql do
      begin
        Clear;
        Add('INSERT INTO formations');
        Add('  (f_numero, f_nom, f_jours, f_prix)');
        DecimalSeparator:= '.';
        Add('  VALUES ('IntToStr(p_number)+ ', 'QuotedStr(p_name)
            + ', 'IntToStr(p_days)+ ', 'FloatToStr(p_cost)+ ')');
        DecimalSeparator:= ',';
      end;

      Try
        ExecSql;
      except
        on eException do
          display('  *** pb_insert 'e.Message);
      end;
    end// with SqlQuery1
  end// insert_generic

Et voici un exemple de procédure appelante

procedure TForm1.insert_generic_Click(SenderTObject);
  begin
    insert_generic(1, 'Initiation Delphi', 3, 1400.40);
    insert_generic(2, 'Bases de Données Delphi', 3, 1400);
    insert_generic(3, 'Oracle Delphi', 3, 1400);
    insert_generic(4, 'Composants Delphi', 3, 1400);
    insert_generic(5, 'UML et Patterns Delphi', 3, 1400);
    insert_generic(4, 'Initiation Pascal', 4, 1900);
  end// insert_generic_Click

Notez que:

  • la procédure appelante pourrait aussi bien lire ses donnée d'une autre source (un fichier FILE OF, un fichier ASCII ("comma separates values" ou autre), un autre table (Oracle, Sql Serveur ou même une autre table Interbase...)
  • le paramètres p_cost est de type Double:
    • la procédure appelante envoie une valeur littérale avec un point décimal

             insert_generic(2, 'Bases de Données Delphi', 3, 1.400);

    • la procédure appelée utilise FloatToStr, qui attend une virgule décimale. Nous forçons donc temporairement l'utilisation du point:

                  DecimalSeparator:= '.';
                  Add('  VALUES ('IntToStr(p_number)+ ... + FloatToStr(p_cost)+ ')');
                  DecimalSeparator:= ',';



Le fichier oracle_dbx_insert_data.zip contient aussi l'ajout utilisant un tEdit, ainsi qu'un ajout depuis un script de paramètres ASCII.



Pour vérifier que nos ajouts ont effectivement réussi, nous avons ajouté à notre application trois boutons qui ouvrent chaque table et comptent simplement le nombre de fiches.

const k_select_formations'SELECT * FROM formations';

procedure select(p_queryString);
  var l_countInteger;
  begin
    with Form1 do
    begin
      with SqlQuery1 do
      begin
        Close;
        Sql.Text:= p_query;
        Open;
        l_count:= 0;
        While not Eof do
        begin
          Inc(l_count);
          Next;
        end// while not Eot

        display('  ok, 'IntToStr(l_count)+ ' lines');
      end// with SqlQuery1
    end// with Form1
  end// select

procedure TForm1.select_Click(SenderTObject);
  begin
    select(k_select_formations);
  end// select_Click

Notez que pour lire les données nous avons utilisé SqlQuery1.Open (alors que pour écrire des données, nous avons utilisé tSqlQuery.ExecSql).



Le véritable affichage du contenu de chaque ligne nous allons le réaliser maintenant, et en utilisant un tClientDataSet.




7 - Affichage des Données

7.1 - Principe de la lecture de données

Nous avons vu que pour écrire des données (créer une table, ajouter des données) SqlQuery envoyait une requête d'écriture, le Serveur effectuait l'écriture, et ne retournait quasiment rien (au mieux un message d'erreur).

La situation est tout à fait différente pour la lecture. L'instruction SELECT a pour seul but de récupérer côté client des données correspondant à une ou plusieurs tables qui existent dans la base de donnée.

Ce qui change, côté Client, c'est qu'il va falloir stocker les lignes de données correspondant à la table constituant la réponse. Certes, il arrive que la réponse ne contienne qu'un entier (SELECT COUNT ...  ), mais même dans ce cas, le résultat est considéré comme une table d'une ligne et d'une colonne. Et par conséquent les composants liés à SqlQuery doivent être prêts à effectuer ce stockage.

Voici le schéma de l'exécution d'un SELECT :

  • le Serveur contient une table "formations"

    image

  • le Client envoie une requête

     SELECT * FROM formations

  • la requête est envoyée vers le Serveur

    image

  • le Serveur construit une NOUVELLE TABLE correspondant à la requête (même si elle sera identique à une table existante)

    image

  • les lignes de cet "answer set" sont envoyés en un ou plusieurs paquets vers le Client:

    image

  • le Client stocke les lignes et les traite:

    image



Le type de stockage côté Client dépend de la suite de composants utilisés:
  • si nous travaillons directement en OCI (Oracle Call Interface : le langage de base du Client Oracle), c'est avant d'envoyer SELECT que nous allouons un tampon mémoire pour la réponse

  • si nous utilisons le BDE (Borland Database Engine: le premier moteur de base de données de Delphi, en 1996), c'est le BDE qui stockait la réponse dans des tampons, et le composant tQuery copiait dans un autre tampon les lignes correspondant au tdbGrid ayant le plus de lignes:

    image

  • si nous utilisons dbExpress (ce qui est le cas dans cet article), le résultat est stocké par le tClientDataSet:

    image



7.2 - Lecture dans un tClientDataSet

Voici notre première application pour afficher les données:
   créez une nouvelle application et nommez-la "p_oracle_dbx_read_data"
   sélectionnez dans la page "dbExpress" de la Palette le composant SqlConnection et posez-le sur la tForm.
Cliquez deux fois sur SqlConnection1, renseignez
     "Driver Name" -> Oracle
     "DataBase" -> ORCL
     "User Name" -> SCOTT
     "Pass Word" -> TIGER
Allez dans l'Inspecteur d'Objet:
  • sélectionnez LoginPrompt et basculez sa valeur sur False
  • vérifiez la connection en basculant Connected sur True, puis fermez la connection en basculant Connected sur False.
   sélectionnez dans la page "dbExpress" de la Palette le composant SqlQuery et posez-le sur la tForm.
  • Sélectionnez sa propriété SqlConnection et initialisez-la à SqlConnection1
  • sélectionnez sa propriété SQL, cliquez sur l'ellipse ... pour ouvrir l'éditeur de chaînes de caractères et tapez-y la requête:

         SELECT * FROM formations

    image

  • cliquez sur "OK" pour ferme l'éditeur
   sélectionnez dans la page "Data Access" de la Palette le composant DatasetProvider:

image

  • Posez-le sur la tForm.
  • dans sa propriété DataSet sélectionnez SqlQuery1
   sélectionnez dans la page "Data Access" de la Palette le composant ClientDataSet:

image

  • Posez-le sur la tForm.
  • dans sa propriété ProviderName sélectionnez DatasetProvider1
   sélectionnez dans la page "Data Access" de la Palette le composant DataSource:

image

  • Posez-le sur la tForm.
  • dans sa propriété DataSet sélectionnez ClientDataSet1
   sélectionnez dans la page "Data Controls" de la Palette le composant dbGrid:

image

  • Posez-le sur la tForm.
  • dans sa propriété DataSource sélectionnez DataSource1
   sélectionnez sur la Forme SqlClientDataSet1, et basculez sa propriété Active à True

   dbExpress envoie la demande d'ouverture à DataSetProvider1, qui la transmet à SqlQuery1, qui connecte la base, recherche les données et les propage par le chemin inverse dans la dbGrid: vous voyez les donnée affichées:

image



Notez l'ordre de chaînage des composants:

Cet ordre provient des multiplicités possibles:

  • une tSqlConnection peut être reliée à plusieurs tSqlQueries
  • un tSqlQuery peut être relié à plusieurs tDatasetProviders
  • un tDataSetProvider peut alimenter plusieurs tClientDatasets
  • un tClientDataSet peut être relié à plusieurs tDataSource
  • un tDataSource peut être relié à plusieurs composants visuels tels que la tdbGrid

Si Delphi avait proposé au développeur programmeur de relier tSqlConnection à ses tSqlQueries, il aurait fallu prévoir une LISTE de tSqlQueries associés, alors que dans l'ordre inverse, chaque tSqlQuery ne peut avoir qu'une tSqlConnection, donc une seule ligne dans l'Inspecteur d'Objet.



En conclusion, SQL comporte deux catégories de requêtes:

  • les requêtes qui écrivent des données sur le Serveur (création, envoi de données, modifications ...). Ces requêtes sont lancées en dbExpress par SqlQuery.ExecSql
  • les requêtes de lecture qui vont recevoir une table réponse du Serveur, et qui sont lancées en dbExpress par SqlQuery.Open (ou son équivalent au niveau de Inspecteur d'Objet: SqlQuery.Active:= True)
SqlQuery.ExecSql n'a aucun équivalent dans l'Inspecteur d'Objet et ne peut être lancé qu'en exécutant le programme (alors que SqlQuery.Active peut être basculé en mode conception)



Vous pouvez télécharger le sources du projet "oracle_dbx_read_data.zip".




8 - Le mode Nomade

8.1 - Principe

Nous avons vu que le tClientDataSet stockait TOUTES les lignes du résultat en mémoire.

Ce stockage choque tout le monde au départ: y aura-t-il toujours assez de place ?

En fait il faut se rendre à l'évidence que l'utilisateur ne travaille jamais sur toutes les données de la base en même temps. Lorsque le préposé d'EDF vient visiter les clients du 9ième, il n'est concerné que par les utilisateurs de cet arrondissement. Il n'a aucun intérêt à télécharger sur son appareil de relevé les 17 millions d'abonnés d'EDF de France: seuls ceux de sa tournée le concernent. Il en est de même pour les autres traitements utilisateurs. C'est donc au développeur de formuler ses requêtes pour ne rappatrier vers le tClientDataSet que les données que l'utilisateur va réellement utiliser.



Dans ces conditions, le tClientDataset est capable de contenir toutes les données à traiter: pour l'affichage, mais aussi pour les modifications.

De plus l'utilisateur peut:

  • sauvegarder les données du tClientDataSet localement dans un fichier du PC client

    image

  • fermer son PC
  • allumer son PC plus tard (en déplacement), recharger les données du tClientDataSet et les utiliser (lecture, modification), puis sauvegarder les modifications:

    image

  • de retour au bureau, rallumer son PC, recharger les données modifiées et mettre à jour les tables du Serveur

    image



8.2 - Sauvegarde du ClientDataSet

Pour démontrer le fonctionnement nomade (BriefCase en anglais), nous allons volontairement utiliser 3 applications séparées, pour bien démontrer que le tClientDataset peut être utilisé sans aucune connection au Serveur Oracle

Tout d'abord, la première application qui charge le tClientDataset depuis le Serveur, et sauvegarde sur disque. Voici comment sauvegarder le contenu du ClientDataSet:
   créez une nouvelle application et nommez-la "p_oracle_dbx_save_cds"
   placez un tSqlConnection sur la Forme
  • Cliquez deux fois sur SqlConnection1, renseignez "Driver Name" (ORACLE), "DataBase" (ORCL), "User Name" (SCOTT)et "Pass Word" (TIGER).
  • dans l'Inspecteur d'Objet, basculez LoginPrompt sur False, et vérifiez la connection en basculant SqlConnection1.Connected sur True, puis fermez la connection.
   sélectionnez dans la page "dbExpress" de la Palette le composant SqlQuery et posez-le sur la tForm.
  • Sélectionnez sa propriété SqlConnection et initialisez-la à SqlConnection1
  • sélectionnez sa propriété SQL, cliquez sur l'ellipse ... pour ouvrir l'éditeur de chaînes de caractères et tapez-y la requête:
         SELECT * FROM FORMATIONS
  • cliquez sur "OK" pour ferme l'éditeur
   sélectionnez dans la page "Data Access" de la Palette le composant DatasetProvider:
  • Posez-le sur la tForm.
  • dans sa propriété DataSet sélectionnez SqlQuery1
   sélectionnez dans la page "Data Access" de la Palette le composant ClientDataSet:
  • Posez-le sur la tForm.
  • dans sa propriété ProviderName sélectionnez DatasetProvider1
   sélectionnez dans la page "Data Access" de la Palette le composant DataSource:
  • Posez-le sur la tForm.
  • dans sa propriété DataSet sélectionnez ClientDataSet1
   sélectionnez dans la page "Data Controls" de la Palette le composant dbGrid:
  • Posez-le sur la tForm.
  • dans sa propriété DataSource sélectionnez DataSource1
   posez un tButton sur la Forme et faites lui ouvrir ClientDataset1:
    ClientDataset1.Open;
   posez un tButton sur la Forme et faites lui sauvegarder le contenu actuel de tClientDataset:
    ClientDataset1.SaveToFile('RESU.XML');

   compilez et exécutez. Ouvrez le tClientDataSet et sauvegardez son contenu
   le tClientDataset sauvegarde les données
   utilisez un explorateur Windows pour vérifier que le fichier est bien sur disque:

image

Si vous cliquez sur RESU.XML, Windows va ouvrir Internet Explorer qui va afficher, tant bien que mal, le contenu de cet fichier .XML (vue partielle):

image

Notez que:

  • l'affichage (tDataSource et tdbGrid) n'est pas nécessaire, mais permet une vérification rapide avant sauvegarde


8.3 - Travail en mode déconnecté (Briefcase)

A présent utilisons le CDS sans être connecté au Serveur:
   créez une nouvelle application et nommez-la "p_oracle_dbx_briefcase"
   sélectionnez dans la page "Data Access" de la Palette le composant ClientDataSet et posez-le sur la tForm.

   sélectionnez dans la page "Data Access" de la Palette le composant DataSource:
  • Posez-le sur la tForm.
  • dans sa propriété DataSet sélectionnez ClientDataSet1
   sélectionnez dans la page "Data Controls" de la Palette le composant dbGrid:
  • Posez-le sur la tForm.
  • dans sa propriété DataSource sélectionnez DataSource1
   posez un tButton sur la Forme et faites lui charger (et ouvrir) ClientDataset1:
    ClientDataset1.LoadFromFile('RESU.XML');Open;

   pour pouvoir sauvegarder vos modifications, posez un tButton sur la Forme et faites lui sauvegarder le contenu actuel de tClientDataset:
    ClientDataset1.SaveToFile('RESU.XML');
   compilez et exécutez. Cliquez "load_cds_"
   la dbGrid affiche le contenu de la table
   à titre d'exercice, ajoutez une nouvelle formation (7, SQL Oracle, 3, 1400):

image

   sauvegardez le Cds avec ses modification en cliquant "save_cds_", puis quittez cette application


8.4 - Mise à jour du Serveur

Pour mettre à jour le Serveur, nous utilisons simplement cds.ApplyUpdates ("effectuez les mises à jour"):
   créez une nouvelle application et nommez-la "p_oracle_dbx_update_server"
   placez un tSqlConnection sur la Forme
  • Cliquez deux fois sur SqlConnection1, renseignez "Driver Name" (ORACLE), "DataBase" (ORCL), "User Name" (SCOTT)et "Pass Word" (TIGER).
  • dans l'Inspecteur d'Objet, basculez LoginPrompt sur False, et vérifiez la connection en basculant SqlConnection1.Connected sur True, puis fermez la connection.
  • créez l'événement OnAfterConnect, et placez-y l'instruction
         SqlConnection1.ExecuteDirect(
             'ALTER SESSION SET NLS_NUMERIC_CHARACTERS=''.,''');
   sélectionnez dans la page "dbExpress" de la Palette le composant SqlQuery et posez-le sur la tForm.
  • Sélectionnez sa propriété SqlConnection et initialisez-la à SqlConnection1
  • sélectionnez sa propriété SQL, cliquez sur l'ellipse ... pour ouvrir l'éditeur de chaînes de caractères et tapez-y la requête:
         SELECT * FROM FORMATIONS
  • cliquez sur "OK" pour ferme l'éditeur
   sélectionnez dans la page "Data Access" de la Palette le composant DatasetProvider:
  • Posez-le sur la tForm.
  • dans sa propriété DataSet sélectionnez SqlQuery1
   sélectionnez dans la page "Data Access" de la Palette le composant ClientDataSet:
  • Posez-le sur la tForm.
  • dans sa propriété ProviderName sélectionnez DatasetProvider1
   sélectionnez dans la page "Data Access" de la Palette le composant DataSource:
  • Posez-le sur la tForm.
  • dans sa propriété DataSet sélectionnez ClientDataSet1
   sélectionnez dans la page "Data Controls" de la Palette le composant dbGrid:
  • Posez-le sur la tForm.
  • dans sa propriété DataSource sélectionnez DataSource1
   posez un tButton sur la Forme et faites lui charger ClientDataset1:
    ClientDataset1.LoadFromFile('RESU.XML');
   posez un tButton sur la Forme et faites lui mettre à jour le Serveur Oracle:
    ClientDataset1.ApplyUpdates(-1);

   compilez et exécutez. Ouvrez le tClientDataSet et sauvegardez son contenu
   le tClientDataset sauvegarde les données sur le Serveur: si vous chargez l'application qui affiche simplement le contenu de FORMATIONS, vous verrez bien la nouvelle ligne 7


Notez que:
  • l'événement SqlConnection1.OnAfterConnect est destiné à faire accepter les décimales françaises. Si vous ne placez pas cette instruction, vous aurez une exception "ORA-01722" (erreur de conversion d'une chaîne numérique, pour des raisons de conflit "," et "." par exemple)
  • nous avons placé dans SqlQuery la requête SELECT  . En effet le .XML ne contient pas le nom de la table
  • le nom de la table est "FORMATIONS" et non pas "formations". Il s'avère que dbExpress passe les noms de tables en majuscules, et donc si nous utilisons "formations", le Serveur provoquera une exception "table pas trouvée"



9 - Modification de Données

9.1 - UPDATE

L'instruction SQL de base pour modifier une donnée est UPDATE.

 UPDATE formations
    SET f_prix= 1450
    WHERE f_numero= 3

Comme cette instruction modifie des données du Serveur, nous utiliserons un tSqlQuery et appellerons ExecSql.
   créez une nouvelle application et appelez-la "p_oracle_dbx_update_data.dpr"

   placez un tSqlConnection sur la Forme
  • Cliquez deux fois sur SqlConnection1, renseignez "Driver Name" (ORACLE), "DataBase" (ORCL), "User Name" (SCOTT)et "Pass Word" (TIGER).
  • dans l'Inspecteur d'Objet, basculez LoginPrompt sur False, et vérifiez la connection en basculant SqlConnection1.Connected sur True, puis fermez la connection.
   sélectionnez dans la page "dbExpress" de la Palette le composant SqlQuery et posez-le sur la tForm.
  • Sélectionnez sa propriété SqlConnection et initialisez-la à SqlConnection1
   placez un tButton sur la Forme et créez sa méthode OnClick. Placez-y les instructions de modification (changement de "Oracle" en "ORACLE":

procedure TForm1.update_Click(SenderTObject);
  begin
    with SqlQuery1 do
    begin
      Close;

      with Sql do
      begin
        Clear;
        Add('UPDATE formations');
        Add('  SET f_nom= 'QuotedStr('ORACLE Delphi'));
        Add('  WHERE f_numero= 3');
      end;

      Try
        ExecSql;
      except
        on eException do
          display('  *** pb_insert 'e.Message);
      end;
    end// with SqlQuery1
  end// update_Click


   compilez, exécutez, et cliquez le bouton


Vous pouvez télécharger le sources du projet "oracle_dbx_update_data.zip".



9.2 - Principe de Modification Interactive

Pour modifier les données en utilisant le tClientDataset, l'utilisateur va travailler en deux étapes:
  • toutes les modifications (ajout, changement, effacement) sont mémorisés par le tClientDataSet
  • lorsqu'il le souhaite, l'utilisateur peut envoyer les modifications vers le Serveur Interbase
L'utilisateur travaille donc dans un premier temps uniquement avec la mémoire du tClientDataSet. Celui-ci garde la trace intégrale de toutes les modifications:
  • si nous modifions un enregistrement, nous aurons 6 fois l'enregistrement en mémoire (l'original et les 5 modifications)
  • les ajouts sont placés dans la mémoire
  • les effacements sont mémorisés comme une nouvelle ligne avec une marque d'effacement.


9.3 - Les Modifications de tClientDataset

Le tClientDataset contient donc une suite de lignes numérotées:
  • les lignes chargées lors de l'ouverture de tClientDataset
  • les lignes ajoutées
  • les versions modifiées de lignes précédentes
  • les lignes effacées
Le dbGrid ne présente quant à lui que la dernière version de chaque ligne.



Pour visualiser les lignes originales et leurs versions successives, nous allons simplement sauvegarder le tClientDataset dans un fichier .XML et réafficher ce fichier .XML. Ce type d'analyse a été présenté dans l'article Affichage ClientDataset XML. Nous nous contenterons ici d'appeler les classes d'analyse et d'affichage.

Par conséquent:
   créez une nouvelle application et appelez-la "p_oracle_dbx_modify_cds.dpr"

   placez un tSqlConnection sur la Forme
  • Cliquez deux fois sur SqlConnection1, renseignez "Driver Name" (ORACLE), "DataBase" (ORCL), "User Name" (SCOTT)et "Pass Word" (TIGER).
  • dans l'Inspecteur d'Objet, basculez LoginPrompt sur False, et vérifiez la connection en basculant SqlConnection1.Connected sur True, puis fermez la connection.
  • créez l'événement OnAfterConnect, et placez-y l'instruction
         SqlConnection1.ExecuteDirect(
             'ALTER SESSION SET NLS_NUMERIC_CHARACTERS=''.,''');
   sélectionnez dans la page "dbExpress" de la Palette le composant SqlQuery et posez-le sur la tForm.
  • Sélectionnez sa propriété SqlConnection et initialisez-la à SqlConnection1
  • sélectionnez sa propriété SQL, cliquez sur l'ellipse ... pour ouvrir l'éditeur de chaînes de caractères et tapez-y la requête:
         SELECT * FROM FORMATIONS
  • cliquez sur "OK" pour ferme l'éditeur
   sélectionnez dans la page "Data Access" de la Palette le composant DatasetProvider:
  • Posez-le sur la tForm.
  • dans sa propriété DataSet sélectionnez SqlQuery1
   sélectionnez dans la page "Data Access" de la Palette le composant ClientDataSet:
  • Posez-le sur la tForm.
  • dans sa propriété ProviderName sélectionnez DatasetProvider1
   sélectionnez dans la page "Data Access" de la Palette le composant DataSource:
  • Posez-le sur la tForm.
  • dans sa propriété DataSet sélectionnez ClientDataSet1
   sélectionnez dans la page "Data Controls" de la Palette le composant dbGrid:
  • Posez-le sur la tForm.
  • dans sa propriété DataSource sélectionnez DataSource1
   posez un tButton sur la Forme et faites lui ouvrir ClientDataset1:
    ClientDataset1.Open;
   ajoutez un tButton pour afficher le contenu actuel du ClientDataset1 (optionnel):
  • ajoutez u_c_cds_analyze_xml et u_cds_table à la liste des USES
  • posez un tButton sur la Forme et tapez le code d'affichage:

    procedure TForm1.display_xml_Click(SenderTObject);
      var l_c_tablec_table;
      begin
        // -- save the CDS with some temporary name
        ClientDataset1.SaveToFile(k_root_path'temp.xml');

        // -- reload the CDS and display it
        with c_cds_analyze_xml.create_cds_analyze_xml('xml',
            k_root_path'temp.xml'do
        begin
          l_c_table:= c_table.create_table('table');
          analyze_xml(l_c_table);

          display_line;
          l_c_table.display_formatted_line_list;
          l_c_table.Free;
          Free;
        end// with c_analyze_xml
      end// display_xml_Click


   posez un tButton sur la Forme et faites lui mettre à jour le Serveur:
    ClientDataset1.ApplyUpdates(-1);
   posez un tButton sur la Forme et faites lui mettre à jour le Serveur:
    ClientDataset1.Close;
   compilez et exécutez


A présent voici un exemple de manipulation:
   cliquez sur le bouton "open_cds_"
   l'application ouvre la table et l'affiche
   changez "ORACLE Delphi" en ORACLE DELPHI"
   descendez verticalement sur la ligne suivante (pour forcer le transfert du dbGrid vers le ClientDataset = POST)
   cliquez "display_xml_" pour visualiser la modification:

image

La ligne 3 est bien inchangée, mais le ClientDataset comporte une ligne supplémentaire spécifiant quelles modifications ont été apportées à la ligne 3

   envoyez les modifications vers les Serveur en cliquant "ApplyUpdates_"
   verifiez la modification en fermant le ClientDataSet ("close_cds_") puis en le rechargeant ("open_cds_")


9.4 - Réconciliation de Données

Si deux agences de voyages essayent simultanément de réserver sur le même vol plus de place qu'il n'en reste, la grogne à l'enregistrement est garantie. Il faut donc trouver un moyen d'arbitrer entre les requêtes incompatibles.

Nous n'allons pas rentrer ici dans le détail des mode de transaction et des verrouillages. Mais voici comment fonctionne une façon de traiter le problème:

  • Oracle (comme les autres moteurs: Interbase, Sql Serveur, DB2, MySql etc) fonctionne en mode "optimiste": plusieurs utilisateurs peuvent charger une ligne, et la modifier. C'est le dernier qui a parlé qui a raison
  • nous pouvons demander à dbExpress de vérifier avant de mette à jour que les valeur actuellement sur le Serveur sont bien les mêmes que celles que nous avons chargées 10 minutes plus tôt (en français clair: un autre utilisateur a-t-il effectué des modifications depuis notre chargement ?). Cela se fait simplement en ajoutant à UPDATE une clause WHERE qui vérifie que la valeur est celle que nous avons chargée avant nos modifications). Pour faire simple, cette clause pourrait être:

     UPDATE formations
        SET f_nom= 'ORACLE DELPHI
        WHERE (f_numero= 3) AND (f_nom= 'ORACLE Delphi')

  • si UPDATE échoue (parce que WHERE ne trouve plus nos valeurs initiale), dbExpress provoque une exception nous indiquant qu'un autre utilisateur a modifié les données
  • notre application a alors la main. Nous pouvons décider de forcer un UPDATE (écrasant la valeur des autres utilisateurs), ignorer l'erreur, envoyer un message à l'utilisateur en lui demandant de recommencer en partant des nouvelles valeurs actuellement sur le Serveur etc
  • pour faciliter la gestion de ces exceptions, dbExpress nous propose un dialogue par défaut affichant à le conflit de résolution et permettant à l'utilisateur de choisir le type de traitement à effectuer (ignorer, écraser etc)


Voyons plus en détail comment gérer les erreur de réconciliation. Il faut tout d'abord créer l'événement OnReconcileError du tClientDataset. Les paramètres sont les suivants:

    PROCEDURE ReconcileError(
        p_c_data_set: TCustomClientDataSet;
        p_c_error: EReconcileError;
        p_update_kind: TUpdateKind;
        VAR pv_action: TReconcileAction);

et:

  • p_c_data_set: c'est le tClientDataSet qui contient les erreurs
  • p_c_error: une exception contenant dans p_c_error.Message le texte de l'exception
  • p_update_kind indique si l'erreur provenant d'un ajout (ukInsert), d'une modification (ukModify) ou d'un effacement (ukDelete)
  • pv_action: indique ce qu'il faut faire de cette erreur (annuler, forcer la valeur etc)
Et Borland suggère même d'utiliser un dialogue tout préparer pour proposer à l'utilisateur de décider ce qu'il faut faire en lui présentant directement le problème et le le laissant choisir le traitement à effectuer. Ce dialogue appelé RECERROR.PAS se trouve dans:

C:\PROGRAM FILES\BORLAND\DELPHI6\OBJREPOS

et a l'allure suivante:

et:

  • il affiche l'erreur
  • présente les données ayant causé la faute
  • laisse l'utilisateur décider ce qu'il souhaite faire
Pour utiliser ce dialogue:
  • il faut créer l'événement tClientDataset.OnReconcileError
  • ouvrir le dialogue RecError et récupérer le choix de l'utilisateur en appelant HandleReconcileError en retournant cette valeur dans pv_action:

         pv_action:= HandleReconcileError(p_c_data_set, p_update_kind, p_c_error);

Finalement pour comprendre les commandes que Delphi génère pour nous lors de l'appel de tClientDataset.ApplyUpdates, nous allons employer un SqlMonitor qui affiche les API Interbase envoyées vers le Serveur Interbase. Pour cela il suffit de
  • relier le tSqlMonitor à tSqlConnection
  • basculer tSqlMonitor sur True
  • créer l'événement tSqlMonitor.OnLogTrace et visualiser tSqlMonitor.TraceList


Créons à présent l'application qui permet les modifications:
   créez une nouvelle application et appelez-la "p_oracle_dbx_reconcile.dpr"

   placez un tSqlConnection sur la Forme
  • Cliquez deux fois sur SqlConnection1, renseignez "Driver Name" (ORACLE), "DataBase" (ORCL), "User Name" (SCOTT)et "Pass Word" (TIGER).
  • dans l'Inspecteur d'Objet, basculez LoginPrompt sur False, et vérifiez la connection en basculant SqlConnection1.Connected sur True, puis fermez la connection.
  • créez l'événement OnAfterConnect, et placez-y l'instruction
         SqlConnection1.ExecuteDirect(
             'ALTER SESSION SET NLS_NUMERIC_CHARACTERS=''.,''');
   sélectionnez dans la page "dbExpress" de la Palette le composant SqlQuery et posez-le sur la tForm.
  • Sélectionnez sa propriété SqlConnection et initialisez-la à SqlConnection1
  • sélectionnez sa propriété SQL, cliquez sur l'ellipse ... pour ouvrir l'éditeur de chaînes de caractères et tapez-y la requête:
         SELECT * FROM FORMATIONS
  • cliquez sur "OK" pour ferme l'éditeur
   sélectionnez dans la page "Data Access" de la Palette le composant DatasetProvider:
  • Posez-le sur la tForm.
  • dans sa propriété DataSet sélectionnez SqlQuery1
  • dans sa propriété UpdateMode, sélectionnez UpWhereAll (ne mettre à jour que si la ligne est exactement celle que nous avons initialement chargée)
   sélectionnez dans la page "Data Access" de la Palette le composant ClientDataSet:
  • Posez-le sur la tForm.
  • dans sa propriété ProviderName sélectionnez DatasetProvider1
   sélectionnez dans la page "Data Access" de la Palette le composant DataSource:
  • Posez-le sur la tForm.
  • dans sa propriété DataSet sélectionnez ClientDataSet1
   sélectionnez dans la page "Data Controls" de la Palette le composant dbGrid:
  • Posez-le sur la tForm.
  • dans sa propriété DataSource sélectionnez DataSource1
   posez un tButton sur la Forme et faites lui ouvrir ClientDataset1:
    ClientDataset1.Open;
   posez un tButton sur la Forme et faites lui mettre à jour le Serveur:
    ClientDataset1.ApplyUpdates(-1);

   ajoutez à la liste des USES la forme de réconciliation:

     USES .... recerror, ...

   créez le gestionnaire d'erreur:
  • sélectionnez ClientDataset1
  • créez son événement OnReconcileError
  • appelez la Forme de réconciliation et retournez le code à Delphi:

    procedure TForm1.ClientDataSet1ReconcileError(
          DataSetTCustomClientDataSetEEReconcileError;
          UpdateKindTUpdateKindvar ActionTReconcileAction);
      begin
        display('reconcile 'e.Message);
        action:= HandleReconcileError(DataSetUpdateKinde);
      end// ClientDataSet1ReconcileError

   ajoutez la trace Sql:
  • sélectionnez dans la page "dbExpress" de la Palette le composant SqlMonitor:

  • Posez-le sur la tForm.
  • sélectionnez SqlConnection et mettez-y SqlConnection1
  • sélectionnez Active et basculez le sur True
  • créez son événement OnLogTrace et affichez les textes:

    var g_trace_countInteger= 0;

    procedure TForm1.SQLMonitor1LogTrace(SenderTObject;
        CBInfopSQLTRACEDesc);
      var l_lengthInteger;
          l_textString;
      begin
        with CBinfodo
        begin
          l_length:= uTotalMsgLen;
          if l_length> 0
            then begin
                SetLength(l_textl_length);
                Movepsztracel_text[1], l_length);
                display(l_text);
              end;
        end;

        with SQLMonitor1TraceList do
          while g_trace_countCount do
          begin
            display(Strings[g_trace_count]);
            Inc(g_trace_count);
          end;
      end// SQLMonitor1LogTrace


   compilez et exécutez


Et pour vérifier le fonctionnement:
   lancez l'exécution, cliquez sur "open_cds_"
   nous chargeons la table, et, entre autre "3, ORACLE DELPHI"

   pour simuler un autre utilisateur, allez dans un explorateur Windows et lancez une seconde instance de p_oracle_dbx_reconcile_cds.exe.
  • cliquez sur "open_cds_"
  • modifiez "3, ORACLE DELPHI" en "3, ORACLE et delphi"
  • descendez verticalement pour poster
  • mettez à jour le Serveur en cliquant "ApplyUpdates_"
  • fermez cette application
   retournez dans la première instance
  • Modifiez "3, ORACLE DELPHI" en "bdd oracle delphi"
  • descendez verticalement pour poster
  • cliquez "ApplyUpdates_"
   Delphi affiche une exception:

   cliquez "OK" pour fermer le dialogue, puis "Run" dans le menu Delphi

   Delphi affiche le dialogue de réconciliation

   choisissez une des options, par exemple "correct" et cliquez "ok"

   Delphi réaffiche la dbGrid, et nous laisse modifier / mettre à jour etc



10 - Et Maintenant ...

Dans ce premier article, nous n'avons pas présenté:
  • la gestion de Serveur (sauvegarde / restauration, les différents modèles Oracle ...)
  • le détail des types de données
  • le détail de traitement de tClientDataset
  • procédures stockées, les triggers, les séqueces
  • traitement détaillé de la réconciliation
  • la gestion des transactions, les modes de transisolation
  • l'édition d'états
  • la gestion des erreurs
De plus:
  • nous proposons des stages de formations pour les personnes qui souhaiteraient approfondir ce qui a été présenté ici. Le but est de rendre les stagiaires rapidement opérationnels et de répondre aux questions qu'ils auraient pu se poser au cours de premiers contacts avec le développement d'applications Oracle.
  • et nous avons aussi compilé une liste de liens Oracle destinée tout particulièrement aux dévelopeurs.



11 - Télécharger le code source

Vous pouvez télécharger:

Ce .ZIP qui comprend:

  • le .DPR, la forme principale, les formes annexes eventuelles
  • les fichiers de paramètres (le schéma et le batch de création)
  • dans chaque .ZIP, toutes les librairies nécessaires à chaque projet (chaque .ZIP est autonaume)
Ces .ZIP, pour les projets en Delphi 6, contiennent des chemins RELATIFS. Par conséquent:
  • créez un répertoire n'importe où sur votre machine
  • placez le .ZIP dans ce répertoire
  • dézippez et les sous-répertoires nécessaires seront créés
  • compilez et exécutez
Ces .ZIP ne modifient pas votre PC (pas de changement de la Base de Registre, de DLL ou autre). Pour supprimer le projet, effacez le répertoire.

La notation utilisée est la notation alsacienne qui consiste à préfixer les identificateurs par la zone de compilation: K_onstant, T_ype, G_lobal, L_ocal, P_arametre, F_unction, C_lasse. Elle est présentée plus en détail dans l'article La Notation Alsacienne



Comme d'habitude:

  • nous vous remercions de nous signaler toute erreur, inexactitude ou problème de téléchargement en envoyant un e-mail à jcolibri@jcolibri.com. Les corrections qui en résulteront pourront aider les prochains lecteurs
  • tous vos commentaires, remarques, questions, critiques, suggestion d'article, ou mentions d'autres sources sur le même sujet seront de même les bienvenus à jcolibri@jcolibri.com.
  • plus simplement, vous pouvez taper (anonymement ou en fournissant votre e-mail pour une réponse) vos commentaires ci-dessus et nous les envoyer en cliquant "envoyer" :
    Nom :
    E-mail :
    Commentaires * :
     

  • et si vous avez apprécié cet article, faites connaître notre site, ajoutez un lien dans vos listes de liens ou citez-nous dans vos blogs ou réponses sur les messageries. C'est très simple: plus nous aurons de visiteurs et de références Google, plus nous écrirons d'articles.



12 - L'auteur

John COLIBRI est passionné par le développement Delphi et les applications de Bases de Données. Il a écrit de nombreux livres et articles, et partage son temps entre le développement de projets (nouveaux projets, maintenance, audit, migration BDE, migration Xe_n, refactoring) pour ses clients, le conseil (composants, architecture, test) et la formation. Son site contient des articles avec code source, ainsi que le programme et le calendrier des stages de formation Delphi, base de données, programmation objet, Services Web, Tcp/Ip et UML qu'il anime personellement tous les mois, à Paris, en province ou sur site client.
Créé: avr-05. Maj: aou-15  148 articles, 471 sources .ZIP, 2.021 figures
Contact : John COLIBRI - Tel: 01.42.83.69.36 / 06.87.88.23.91 - email:jcolibri@jcolibri.com
Copyright © J.Colibri   http://www.jcolibri.com - 2001 - 2015
Retour:  Home  Articles  Formations  Développement Delphi  Livres  Pascalissime  Liens  Download
l'Institut Pascal

John COLIBRI

+ Home
  + articles_avec_sources
    + bases_de_donnees
      + programmation_oracle
        – installation_10g
        – create_database_10g
        – oracle_tutorial
      + interbase
      + sql_server
      + firebird
      + mysql
      + xml
      – paradox_via_ado
      – mastapp
      – delphi_business_objects
      – clientdataset_xml
      – data_extractor
      – rave_report_tutorial
      – visual_livebindings
      – migration_bde
    + web_internet_sockets
    + prog_objet_composants
    + office_com_automation
    + colibri_utilities
    + uml_design_patterns
    + graphique
    + delphi
    + outils
    + firemonkey
    + vcl_rtl
    + colibri_helpers
    + colibri_skelettons
  + formations
  + developpement_delphi
  + présentations
  + pascalissime
  + livres
  + entre_nous
  – télécharger

contacts
plan_du_site
– chercher :

RSS feed  
Blog

Audit Delphi Bilan technique (technologie, méthodes, architecture, organisation, techologie et version, formation, gestion de projet) et recommandations - Tél 01.42.83.69.36
Formation Rave Report Constructions d'états, avec prévisualisation, génération .HTML ou .PDF - 2 jours
Formation Ecriture de Composants Delphi Creation de composants : étapes, packages, composants, distribution - 3 jours