IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

 Firebird Discussion :

BCB-FireBird Gestion des Clés-Cache de mise a jour


Sujet :

Firebird

  1. #1
    Membre régulier
    Profil pro
    Inscrit en
    Juin 2002
    Messages
    218
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2002
    Messages : 218
    Points : 91
    Points
    91
    Par défaut BCB-FireBird Gestion des Clés-Cache de mise a jour
    Bonjour,
    J'écris un petite application avec:
    -des caches de mise a jour(TClientDataSet et TDataSetProvider)

    - J'ai deux tables liées entre elle via un cle (ID) dans un SGBD FireBird
    -Chaque cle primaire sur chaque table sont auto incrémenté via un générateur

    -Dans C++Builder j'ai établie une liaison Maitre/detail via les 2 caches(1 sur chaque table)
    -Les données sont affichées via deux Dbgrid

    Lorsque je travaille en local , je me coupe du réseau
    je voudrais savoir comment vous gérez les index avant de faire les mise
    a jour sur le SGBD ?
    Lorsque je travaille en local et que j'essaie d'ajouter des valeurs, du fait de ma relation maitre/détail j'ai un exception déclenchée :

    Exception EDatabaseError avec le message 'Le champ 'ID_PERS' doit avoir une valeur'.
    Comment gérez vous ceci ?
    - Est ce que vous faite de requête sql pour savoir le nb d'enreg cote serveur?
    - Est ce que vous calculez l'ID max et est ce vous l'utilisez par programmation coté client via BCB6 ?

    Je vous remercie par avance
    Outils utilisés : FireBird 2.1 - IbExert Free - C++ Builder 6 Pro Update 4- Windows Xp pro Sp3

  2. #2
    Membre éclairé Avatar de freud
    Homme Profil pro
    Développeur
    Inscrit en
    Mai 2002
    Messages
    1 271
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Algérie

    Informations professionnelles :
    Activité : Développeur

    Informations forums :
    Inscription : Mai 2002
    Messages : 1 271
    Points : 681
    Points
    681
    Par défaut
    Bonjour,

    Cela ne fait pas longtemps que j'ai commencer à utiliser le TClientDataset surtout que depuis que j'ai commencer avec FB et je n'ai pas de problème en travaillant en mode déconnecté.

    Personnellement, je ne fais pas de requête coté serveur pour obtenir le max d'enregistrements pour l'insertion.

    Il faut garder le moins longtemps possible des transactions ouvertes en sollicitant de moins en moins le serveur quand cela est possible.
    En récupérant le max depuis le serveur c'est à dire que le générateur
    nous renvois sa valeur+1 et si on a plusieurs clients en même temps qui demande ce max et que certains d'eux annule la requête, alors on se retrouvera avec des trous d'ID.
    En mode déconnecter, le max de l'identifiant de chaque table je le récupère en local et l'incrémente à chaque insert d'un enregistrement et ce, soit dans l'evenement OnBeforeInsert du TclientDataSet ou lorsque je fais :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
     clientDataSet.Insert;
     clientDataSet.FielByName('ID_PERS').Value:=Id
    Ce qui permettra de garder, localement, la relation maitre/detail.
    Les identifiants changeront par la suite via un générateur que vous aurez
    créer au niveau de la BDD lorsque vous démarrerez la transaction après le commit et un refresh pour récupérerer les enregistrements insérées avec leurs nouveaux identifiants.
    Dans une même transaction, j'envoie en premier lieu l'enregistrement maitre et je récupérer en retour son identifiant pour que je puisse l'affecter à la clé étrangère des enregistrements de la table secondaire qui seront envoyés toujours dans la même transaction.

    C'est la méthode que j'utilise actuellement et j'ai pas de problème avec. Je ne sais pas si il y a mieux que ça.
    Si quelqu'un t'a offensé, ne cherche pas à te venger; assieds-toi au bord de la rivière et, bientôt, tu verras passer son cadavre.

    Lao Tseu - un sage chinois

    Celui qui lutte contre les monstres doit veiller à ne pas le devenir lui-même.
    Et quand ton regard pénètre longtemps au fond d'un abîme, l'abîme, lui aussi, pénètre en toi.

    Friedrich Nietzsche - Par délà le bien et le mal

  3. #3
    Membre régulier
    Profil pro
    Inscrit en
    Juin 2002
    Messages
    218
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2002
    Messages : 218
    Points : 91
    Points
    91
    Par défaut
    merci je vais voir de ce cote la....

    cela marche chez moi mais sur l'évènement AfterInsert


    Comment modifiier le générateur pour les initialiser a 0 par une procédure stockée ?

    merci
    Outils utilisés : FireBird 2.1 - IbExert Free - C++ Builder 6 Pro Update 4- Windows Xp pro Sp3

  4. #4
    Membre éclairé Avatar de freud
    Homme Profil pro
    Développeur
    Inscrit en
    Mai 2002
    Messages
    1 271
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Algérie

    Informations professionnelles :
    Activité : Développeur

    Informations forums :
    Inscription : Mai 2002
    Messages : 1 271
    Points : 681
    Points
    681
    Par défaut
    Bonjour,

    Citation Envoyé par o_live
    ....mais sur l'évènement AfterInsert
    Vous pouvez toujours le faire après avoir invoqué la méthode Insert comme indiqué ci-dessus.

    Pour modifier la valeur d'un générateur , il semblerais que les commandes DDL (create,alter,...) ne sont pas accepter dans une PS il faut utiliser à la place un EXECUTE STATEMENT dans une PS :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    EXECUTE STATEMENT  'ALTER SEQUENCE NOM_DU_GENERATEUR  RESTART WITH 0 ';
    Ou sinon en passant en paramètre le nom du générateur et sa valeur :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    EXECUTE STATEMENT 'ALTER SEQUENCE ' || GENERATEUR || ' RESTART WITH ' || CAST(VALEUR AS INTEGER);
    Si quelqu'un t'a offensé, ne cherche pas à te venger; assieds-toi au bord de la rivière et, bientôt, tu verras passer son cadavre.

    Lao Tseu - un sage chinois

    Celui qui lutte contre les monstres doit veiller à ne pas le devenir lui-même.
    Et quand ton regard pénètre longtemps au fond d'un abîme, l'abîme, lui aussi, pénètre en toi.

    Friedrich Nietzsche - Par délà le bien et le mal

  5. #5
    Membre régulier
    Profil pro
    Inscrit en
    Juin 2002
    Messages
    218
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2002
    Messages : 218
    Points : 91
    Points
    91
    Par défaut
    Bonjour
    merci vraiment des première réponse

    J'ai plusieurs petites questions:

    Ou trouver des tutoriel pour le langage spécifique (DDL,Procedure Stockée,etc) à Firebird ? Je connais déjà SQL

    Pour le MAX (coté client) ,voici comment je procède :
    le contrôle qui me sert a valider est un DBNavigator

    As tu mieux a me proposer ?

    1:Fonction Max :

    Code C++ : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    int __fastcall TDataModule1::MAX(TDataSet * pDS, const AnsiString FieldName)
    {
       //FieldName Nom du champ contenant la clé primaire
     
       int MAX=-1;
       TBookmark BM=pDS->GetBookmark();
       /* alloue un marque afin de ne pas bouger  le curseur */
     
       try{
     
         pDS->DisableControls();  // désactive le contrôle
     
         pDS->First();
         MAX=pDS->FieldByName(FieldName)->AsInteger;
     
         while(!pDS->Eof){
     
            int Valeur=pDS->FieldByName(FieldName)->AsInteger ;
     
             if (Valeur>MAX){
               MAX= Valeur;
             }
     
             pDS->Next();
         }
     
     
        }
     
       __finally {
     
         pDS->GotoBookmark(BM);   // on a la marque
         pDS->FreeBookmark(BM);   // libération
         pDS->EnableControls();  //activation des contrôles
         return MAX;
       }
     
     
    }

    2: Sur le constructeur de mon datamodule
    Code c++ : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    __fastcall TDataModule1::TDataModule1(TComponent* Owner)
            : TDataModule(Owner)
    {
    ID_MAX_PERS=0;
    ID_PERS_LU=MAX(CDSPers,"ID_PERS");
    }

    3: Pour générer un ID+1 sur AfterInsert

    Code c++ : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    void __fastcall TDataModule1::CDSPersAfterInsert(TDataSet *DataSet)
    {
     //auto incrémente le champ ID_Pers(cle primaire)
     ID_PERS_LU=ID_MAX_PERS=ID_PERS_LU+1;
     CDSPers->FieldByName("ID_PERS")->AsInteger=ID_MAX_PERS;
    }


    merci encore
    Outils utilisés : FireBird 2.1 - IbExert Free - C++ Builder 6 Pro Update 4- Windows Xp pro Sp3

  6. #6
    Membre éclairé Avatar de freud
    Homme Profil pro
    Développeur
    Inscrit en
    Mai 2002
    Messages
    1 271
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Algérie

    Informations professionnelles :
    Activité : Développeur

    Informations forums :
    Inscription : Mai 2002
    Messages : 1 271
    Points : 681
    Points
    681
    Par défaut
    Pour les tutoriels

    Il y a pas mieux que la base de données Employee.fdb pour commencer.
    Elle se trouve dans le dossiers examples\empbuild de Firebird.
    Elle contient de bons exemples simplifiés sur les PS et les Triggers.

    Voir Introduction au langage SQL avec le SGBD Firebird


    Voir les manuels d'interbase 6.0 & Firebird sur le site officiel de Firebird

    il y a peu de différences entre Interbase et Firebird puisque le 2eme est issu du 1er. La version française de la doc d'Interbase 6 existe je ne souviens plus à quel endroit faire une petite recherche sur google

    Des cours et tutoriaux sur Interbase/Firebird sur developpez.com

    La faq ça aide beaucoup Ici

    Une très bonne faq en anglais avec des How's to Ici

    Voir les tutoriels de Colibri sur Interbase/Firebird enrichis d'exemples avec Delphi Ici

    Des outils gratuits pour gérer les Bases de données

    Flamerobin outil multi-plateformes

    IBExpert Edition personnelle


    Pour le calcul du Max de l'identifiant


    -soit créer un index dans le composant TclientDataset (clic droit) dans la propriété IndexDefs, nommée l'index et affecter à la propriété Field le champ autoIncrement et dans IndexName sélectionner le nom de l'index créer.
    Ensuite faire Table.Last et récupérer la dernier valeur du champ qui sera
    la valeur maximale

    -soit dans la propriété aggregates et dans la petite fenêtre qui apparait
    vous donnez un nom dans AggregateName par ex MaxId et dans Expression
    utiliser la fonction MAX(champ_identifiant) et récupérer le max de l'id ainsi :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    CDSPers.AggregatesActive:=true;
    ID_PERS_LU=CDSPers.Aggregates[0].Value+1;
    Dans ces deux méthodes il y a moins de code à faire. Mais votre méthode est également valable si vous arrivez à récupérer le Max.

    En espérant vous avoir donner entière satisfaction.
    Si quelqu'un t'a offensé, ne cherche pas à te venger; assieds-toi au bord de la rivière et, bientôt, tu verras passer son cadavre.

    Lao Tseu - un sage chinois

    Celui qui lutte contre les monstres doit veiller à ne pas le devenir lui-même.
    Et quand ton regard pénètre longtemps au fond d'un abîme, l'abîme, lui aussi, pénètre en toi.

    Friedrich Nietzsche - Par délà le bien et le mal

  7. #7
    Membre expert

    Homme Profil pro
    Consultant spécialité Firebird
    Inscrit en
    Mai 2002
    Messages
    2 342
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 60
    Localisation : France

    Informations professionnelles :
    Activité : Consultant spécialité Firebird
    Secteur : Conseil

    Informations forums :
    Inscription : Mai 2002
    Messages : 2 342
    Points : 3 712
    Points
    3 712
    Par défaut
    Citation Envoyé par o_live Voir le message
    - Est ce que vous calculez l'ID max
    pourquoi faire ?
    Philippe Makowski
    IBPhoenix - Firebird
    Membre de l'April

  8. #8
    Membre régulier
    Profil pro
    Inscrit en
    Juin 2002
    Messages
    218
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2002
    Messages : 218
    Points : 91
    Points
    91
    Par défaut
    Encore un grand merci a toi Freud pour tes infos
    j'ai adopte ta seconde méthode qui est très efficace et surtout
    qui permet de gérer moins de code

    le code change légèrement en BCB6pro
    Code c++ : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    CDSPers->AggregatesActive=true;
    int ID_MAX_PERS=CDSPers->Aggregates->Items[0]->Value()+1 ;
    CDSPers->FieldByName("ID_PERS")->AsInteger=ID_MAX_PERS;





    merci pour le liens!

    pourquoi faire ?
    dans mon appli j'ai deux tables a gérer , elle sont liées par un intégrité révérencielle :
    -Table Personne clé primaire ID_PERS....etc
    -Table Téléphone clé primaire ID_TEL , clé étrangère #ID_PERS...etc
    -sur chaque table il y a des générateurs auto incrémenté pour ID_PERS et ID_TEL

    avec comme règle Personne.ID_PERS=Téléphone.ID_PERS.

    De plus J'ai deux grilles (DbGrid) dans C++Builder qui sont liées
    via un relation Maitre/Détail -Les données sont stockée dans des caches

    Pour chaque insert je dois fournir impérativement un ID_PERS et un ID_TEL afin de ne pas lever un exception d'où mon calcul du max pour preremplir un champ

    Comment faites cela dans une apllication Delphi ou C++Builder pour faire ceci ?En vous remerciant par avance de votre précieuse dans le passé ainsi que de votre réponse
    Outils utilisés : FireBird 2.1 - IbExert Free - C++ Builder 6 Pro Update 4- Windows Xp pro Sp3

  9. #9
    Membre expert

    Homme Profil pro
    Consultant spécialité Firebird
    Inscrit en
    Mai 2002
    Messages
    2 342
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 60
    Localisation : France

    Informations professionnelles :
    Activité : Consultant spécialité Firebird
    Secteur : Conseil

    Informations forums :
    Inscription : Mai 2002
    Messages : 2 342
    Points : 3 712
    Points
    3 712
    Par défaut
    Citation Envoyé par o_live Voir le message
    Pour chaque insert je dois fournir impérativement un ID_PERS et un ID_TEL afin de ne pas lever un exception
    l'erreur est là
    ce n'est pas normal, c'est le serveur qui à l'aide du trigger et de la sequence qui doit remplir ces champs, pas ton appli
    ton appli est mal conçue à ce niveau
    Philippe Makowski
    IBPhoenix - Firebird
    Membre de l'April

  10. #10
    Membre régulier
    Profil pro
    Inscrit en
    Juin 2002
    Messages
    218
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2002
    Messages : 218
    Points : 91
    Points
    91
    Par défaut
    Merci de votre réponse
    auriez vous un petit exemple sur une table ?
    Outils utilisés : FireBird 2.1 - IbExert Free - C++ Builder 6 Pro Update 4- Windows Xp pro Sp3

  11. #11
    Expert éminent sénior
    Avatar de Cl@udius
    Homme Profil pro
    Développeur Web
    Inscrit en
    Février 2006
    Messages
    4 878
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 61
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Développeur Web
    Secteur : High Tech - Matériel informatique

    Informations forums :
    Inscription : Février 2006
    Messages : 4 878
    Points : 10 008
    Points
    10 008
    Par défaut
    Citation Envoyé par o_live Voir le message
    auriez vous un petit exemple sur une table ?
    Comme dit Philippe, laisse le serveur faire ce qui est son travail.
    L'appel à un générateur se fait hors transaction ce qui assure l'unicité de la valeur, même (et surtout) lors d'accès concurrentiels.

    Puisque tu es sous FB2.1, utilise la clause RETURNING lorsque tu effectues un INSERT dans un table.

    Voir les notes de versions: The RETURNING Clause.

    Claudius

  12. #12
    Membre éclairé Avatar de freud
    Homme Profil pro
    Développeur
    Inscrit en
    Mai 2002
    Messages
    1 271
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Algérie

    Informations professionnelles :
    Activité : Développeur

    Informations forums :
    Inscription : Mai 2002
    Messages : 1 271
    Points : 681
    Points
    681
    Par défaut
    Citation Envoyé par Cl@udius Voir le message
    Puisque tu es sous FB2.1, utilise la clause RETURNING lorsque tu effectues un INSERT dans un table.
    Le problème c'est qu'en premier lieu l'INSERT doit se faire en mode déconnecter du serveur, comme il le précise :

    Citation Envoyé par o_live Voir le message
    Lorsque je travaille en local , je me coupe du réseau
    Mais là, non seulement déconnecter du serveur mais du réseau aussi.
    Donc, son souci c'est de pouvoir maintenir une relation maitre/détail entre deux tables dans lesquelles il fera des Insert, des Edit et des Delete.
    N'étant pas connecter au réseau, il faudra bien qu'il puisse maintenir cette relation par un système de clés d'où la fonction d'incrémentation temporaire (puisque comme il le précise aussi Chaque cle primaire sur chaque table sont auto incrémenté via un générateur) des clés en local avant que les données ne puisse être envoyer au serveur ou d'être sauvegarder sur disque en différant leurs envois. A moins que le TclientDataSet offre un système d'identifiants interne propre à lui que je ne connais pas ...........(????)
    Si quelqu'un t'a offensé, ne cherche pas à te venger; assieds-toi au bord de la rivière et, bientôt, tu verras passer son cadavre.

    Lao Tseu - un sage chinois

    Celui qui lutte contre les monstres doit veiller à ne pas le devenir lui-même.
    Et quand ton regard pénètre longtemps au fond d'un abîme, l'abîme, lui aussi, pénètre en toi.

    Friedrich Nietzsche - Par délà le bien et le mal

  13. #13
    Expert éminent sénior
    Avatar de Cl@udius
    Homme Profil pro
    Développeur Web
    Inscrit en
    Février 2006
    Messages
    4 878
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 61
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Développeur Web
    Secteur : High Tech - Matériel informatique

    Informations forums :
    Inscription : Février 2006
    Messages : 4 878
    Points : 10 008
    Points
    10 008
    Par défaut
    Citation Envoyé par freud Voir le message
    Le problème c'est qu'en premier lieu l'INSERT doit se faire en mode déconnecter du serveur, comme il le précise
    Certes je n'avais pas relevé ce "détail".
    Mais il faudra bien à un moment ou à un autre que o_live applique ces INSERTs.
    D'un autre côté je n'utilise pas le TClientDataSet beaucoup trop lourd (et lent) à mon goût.

  14. #14
    Membre régulier
    Profil pro
    Inscrit en
    Juin 2002
    Messages
    218
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2002
    Messages : 218
    Points : 91
    Points
    91
    Par défaut Plus de details.....
    Bonsoir a tous

    le modèle physique de données est comme ceci (on fait avec le moyens du bord .... )


    Table Personne
    ID_PERS (cle primaire)
    PRENOM
    NOM


    Table Telephonne
    ID_PERS#
    ID_TEL (cle primaire)
    NUMERO
    TYPE

    Ces 2 table sont liées avec cette regle de gestion Personne.ID_PERS=Telephonne.ID_PERS


    Voici cote client en detail (C++Builder )
    mon module de donnée


    Code C++ : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    TIBDatabase *IBDatabase // compo de connection
    TIBTransaction *IBTransaction1;


    Partie MAITRE Table Personne
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    TIBQuery *IBQPers; // Première requête interrogation 
    sur table Personne comme ceci
    
    SELECT  ID_PERS, NOM, PRENOM, ADRESSE 
    FROM PERSONNE
    
    TDataSetProvider *DSPPers // fournisseur
    DSPPers->DataSet=IBQPers (pointe sur IBQPers )
    
    TClientDataSet *CDSPers; // 1 cache qui stocke les données en local
    CDSPers->ProviderName=DSPPers(pointe sur DSPPers)
    
    TDataSource *DSPers ;// alimente ma première grille DBGPers
    DSPers->DataSet=CDSPers

    Partie Detail Table Telephonne


    Code C++ : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    TIBQuery *IBQTel // seconde requete d'interrogation :
    
    SELECT ID_TEL,ID_PERS,NUMERO, TYPE FROM TELEPHONNE
     WHERE ID_PERS=:ID_PERS (paramètre entrée délivré par la première requête SQL)
    
    
    TDataSetProvider *DSPTel // fournisseur
    DSPTel->DataSet=IBQTel
    
    TClientDataSet *CDSTel; //2 cache qui stocke les données en local 
    a de nouvelle propriete par rapport a l'ancien
    
    CDSTel->ProviderName=DSPTel
    
    
    // liaison maitre-Détails
    CDSTel->MasterSource=DSPers(compo fournisseur Pers)
    
    CDSTel->MasterFields==>(Table Personne)ID_PERS->(Table Telephone)ID_PERS
    (Mode conception :jointure entre les 2 ensembles)
    
    
    TClientDataSet *CDSTel // alimente mon seconde grille DBGTel
    CDSTel->DataSet=CDSTel;

    Âpres avoir charger les infos et m'être déconnecté du réseau
    Lorsque je clique sur une personne j'ai tous ses téléphones les deux grilles sont synchronisées

    Les données de chaque table sont stockées dans chacun des caches CDSPers et CDSTel


    Si je veux insérer un nouvel enregistrement coté client, en mémoire cache comme ceci
    Code c++ : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    DataModule1->CDSPers->Insert();
    // bla bla
    ...
    ..
    ...
    puis après compléter,avec les valeurs saisies via ma première grille DBGPers

    Question 1:Comment est ce que je peux dire au serveur FB de me donner un nouvel ID ?

    -Si je ne donne pas de ID_PERS correct j'ai une exception qui se léve

    Citation Envoyé par C++Builder
    ---------------------------
    Notification d'une exception du débogueur
    ---------------------------
    Le projet Annuaire_TClientDataSet_Multitable_champ_auto.exe a provoqué une classe d'exception EDatabaseError avec le message 'Le champ 'ID_PERS' doit avoir une valeur'. Processus stoppé. Utilisez Pas-à-pas ou Exécuter pour continuer.
    ---------------------------
    OK Aide
    ---------------------------


    -Le nouveau enregistrement sera validé en mémoire cache après avoir fait appel de la méthode CDSPers->Post()

    -Les Données ne seront Appliquées définitivement inscrites dans le SGBD qu'après avoir faits appel de la méthode CDSPers->ApplyUpdates()




    Question 2 : j'ai vu sur le composant IBQuery une propriété GeneratorField comment utilise ton cette propriété ?
    Qui peut m'expliquer ceci ?

    Je vous remercie de m'avoir lu est espère avoir été plus clair

    Remarque :
    Lorsque je fais des requêtes de type Insert je me sert du composant StoredProc pour les procédure stockée .Je ne mentionne pas ID_PERS puisque le générateur me donne un nouvel ID_PERS a chaque insertion
    Outils utilisés : FireBird 2.1 - IbExert Free - C++ Builder 6 Pro Update 4- Windows Xp pro Sp3

  15. #15
    Membre expert

    Homme Profil pro
    Consultant spécialité Firebird
    Inscrit en
    Mai 2002
    Messages
    2 342
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 60
    Localisation : France

    Informations professionnelles :
    Activité : Consultant spécialité Firebird
    Secteur : Conseil

    Informations forums :
    Inscription : Mai 2002
    Messages : 2 342
    Points : 3 712
    Points
    3 712
    Par défaut
    Question 1:Comment est ce que je peux dire au serveur FB de me donner un nouvel ID ?
    sans te connecter au réseau tu ne peux pas
    sinon il suffit d'interroger la sequence (ou générateur c'est la même chose)
    Philippe Makowski
    IBPhoenix - Firebird
    Membre de l'April

  16. #16
    Membre expert
    Avatar de Barbibulle
    Profil pro
    Inscrit en
    Octobre 2002
    Messages
    2 048
    Détails du profil
    Informations personnelles :
    Âge : 53
    Localisation : France

    Informations forums :
    Inscription : Octobre 2002
    Messages : 2 048
    Points : 3 342
    Points
    3 342
    Par défaut
    Bonjour,
    Je n'utilise pas de TClientDataset ni de mode déconnecté. Mais il faut comprendre une chose, c'est lorsque vous vous reconnectez pour insérer de nouvelles personnes il vous faut utiliser les générateurs de firebird. C'est le moyen le plus sur de ne pas avoir de message d'erreur sur le clé primaire.

    S'il n'y a qu'une seule personne qui utilise la base de données, utiliser max() fonctionnera mais c'est une très mauvaise pratique. Et elle vous empêchera d'utiliser votre application en multi-poste et dans ce cas pourquoi utiliser une serveur ? autant utiliser firebird embedded..

    Donc nous n'avons pas le choix, il faut utiliser les générateurs. Ceux-ci n'étant accessibles que lorsque l'on est connecté il vous faudra gérer les clés en local lorsque vous n'êtes pas connectés.

    Donc en mode déconnecté, lorsque vous voulez insérer vous générez vos identifiants (avec max si vous le voulez).

    Lorsque vous vous connectez vous allez synchroniser les données avec "ApplyUpdates".
    L'évènement "BeforeUpdateRecord" est lancé avant chaque mise à jour, c'est dans ce gestionnaire d'évènement qui vous faudra identifier les insertions (UpdateKind=ukInsert) et donc demander un identifiant au serveur (select gen_id(gen_personne, 1) from rdb$database;) et remplacer l'identifiant de l'insert par celui du serveur. Enfin mettre a jour les identifiants de la table esclave (téléphones).

  17. #17
    Membre éclairé Avatar de freud
    Homme Profil pro
    Développeur
    Inscrit en
    Mai 2002
    Messages
    1 271
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Algérie

    Informations professionnelles :
    Activité : Développeur

    Informations forums :
    Inscription : Mai 2002
    Messages : 1 271
    Points : 681
    Points
    681
    Par défaut
    Barbibulle l'a clairement expliquer mais comme il dit qu'il est coupé du réseau
    donc on ne peut donc que supposer qu'il n'a pas accés au serveur comme il le veut.


    Citation Envoyé par o_live Voir le message
    Lorsque je travaille en local et que j'essaie d'ajouter des valeurs, du fait de ma relation maitre/détail j'ai un exception déclenchée :
    Exception EDatabaseError avec le message 'Le champ 'ID_PERS' doit avoir une valeur'.
    Mais ça, je suppose que vous l'avez déjà régler avec la méthode des Aggregates qui vous permet de récupérer les valeurs maximales de vos ID's et de les incrémenter à chaque insertion en locale puisque comme vous dites vous êtes coupé du réseau donc du serveur ?
    Vous obtiendrais vos ID's définitif (mise à jour des identifiant) que lorsque vous vous reconnecter au serveur pour l'envoi des données En ayant au préalable créer un Trigger Before Insert pour générer vos ID's comme ceci :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    CREATE OR ALTER TRIGGER NOM_DU_TRIGGER
    ACTIVE BEFORE INSERT POSITION 0
    AS
    begin
      IF (NEW.ID_PERS IS NULL) then
      NEW.ID_PERS = GEN_ID(le_nom_du_generateur,1);  
    end
    Maintenant la question est de savoir es-ce que vous avez la possibilité de vous reconnecter au serveur instantanément, au moment que vous le vouliez ?

    si oui :
    alors a l'insertion se fera directement vers le serveur il sera donc inutile de générer des ID's maitre/detail localement du fait que le serveur le fera lui-même à l'aide du trigger. Et comme l'a dit Cl@udius, utilisez la clause RETURNING pour récupérer l'ID maitre afin de renseigner le champ ID_PERS de la table Telephonne lorsque vous posterez les données de cette table.

    si non :
    alors vous pouvez maintenir la méthode d'incrémentation des ID's en local lorsque vous procéder à des insertions et ce, pour le maintien des enregistrements du maitre avec son détail en attendant que vous ayez accés au serveur et mettre à jour vos identifiants.

    Citation Envoyé par Barbibulle Voir le message
    S'il n'y a qu'une seule personne qui utilise la base de données, utiliser max() fonctionnera mais c'est une très mauvaise pratique. Et elle vous empêchera d'utiliser votre application en multi-poste et dans ce cas pourquoi utiliser une serveur ? autant utiliser firebird embedded..
    Quant à la fonction Max(), celle-ci est utiliser à l'aide de la propriété Aggregates du CDS au niveau local et non le Max à travers une requête SQL depuis le serveur. Je ne vois donc pas en quoi c'est une très mauvaise pratique.??? empêchant l'utilisation de l'application en multi-postes.
    D'autant plus que l'appel au Max ne se fera qu'une fois et stocké par exemple dans une variable public.
    Si quelqu'un t'a offensé, ne cherche pas à te venger; assieds-toi au bord de la rivière et, bientôt, tu verras passer son cadavre.

    Lao Tseu - un sage chinois

    Celui qui lutte contre les monstres doit veiller à ne pas le devenir lui-même.
    Et quand ton regard pénètre longtemps au fond d'un abîme, l'abîme, lui aussi, pénètre en toi.

    Friedrich Nietzsche - Par délà le bien et le mal

  18. #18
    Membre régulier
    Profil pro
    Inscrit en
    Juin 2002
    Messages
    218
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2002
    Messages : 218
    Points : 91
    Points
    91
    Par défaut
    Merci de vos conseil précieux a tous

    Comme dit Barbibulle,je crois qu'il est judicieux d'interroger les générateur au dernier moment pour être sur d'avoir les bonnes valeur des ID dans le Update surtout dans un mode client/Serveur.

    Le composant IBQuery a une propriété GeneratorField qui liste les générateurs pressent dans la base est ce que cela ne pourrait pas être une solution ?
    comment l'utiliser ?

    Feud :dans ma dernière version j'avais réglé via Aggregate le Pb dans mon ancienne version mais dans la nouvelle non Mais je vais y arriver !

    Encore merci a tous
    Outils utilisés : FireBird 2.1 - IbExert Free - C++ Builder 6 Pro Update 4- Windows Xp pro Sp3

  19. #19
    Membre expert
    Avatar de Barbibulle
    Profil pro
    Inscrit en
    Octobre 2002
    Messages
    2 048
    Détails du profil
    Informations personnelles :
    Âge : 53
    Localisation : France

    Informations forums :
    Inscription : Octobre 2002
    Messages : 2 048
    Points : 3 342
    Points
    3 342
    Par défaut
    Citation Envoyé par freud Voir le message
    Quant à la fonction Max(), celle-ci est utiliser à l'aide de la propriété Aggregates du CDS au niveau local et non le Max à travers une requête SQL depuis le serveur. Je ne vois donc pas en quoi c'est une très mauvaise pratique.??? empêchant l'utilisation de l'application en multi-postes.
    D'autant plus que l'appel au Max ne se fera qu'une fois et stocké par exemple dans une variable public.
    Quelque soit la méthode de recherche du max (par SQL, par recherche dans l'ensemble de données, par fonction d'aggregates locale) cela ne vous permettra jamais d'avoir un identifiant unique dans un environnement transactionnel multi-poste.

  20. #20
    Membre éclairé Avatar de freud
    Homme Profil pro
    Développeur
    Inscrit en
    Mai 2002
    Messages
    1 271
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Algérie

    Informations professionnelles :
    Activité : Développeur

    Informations forums :
    Inscription : Mai 2002
    Messages : 1 271
    Points : 681
    Points
    681
    Par défaut
    Bonjour,

    Citation Envoyé par Barbibulle Voir le message
    Quelque soit la méthode de recherche du max (par SQL, par recherche dans l'ensemble de données, par fonction d'aggregates locale) cela ne vous permettra jamais d'avoir un identifiant unique dans un environnement transactionnel multi-poste.
    Mais pas du tout, il n'est nullement question d'aller chercher des identifiants en dehors du serveur de BDD. D'ailleurs, cela à été clairement défini au début du post :

    Citation Envoyé par Freud Voir le message
    Ce qui permettra de garder, localement, la relation maitre/detail.
    Les identifiants changeront par la suite via un générateur que vous aurez
    créer au niveau de la BDD lorsque vous démarrerez la transaction après le commit et un refresh pour récupérerez les enregistrements insérées avec leurs nouveaux identifiants.
    La méthode préconisée d'auto-génération de clés au niveau du client est temporaire. Du fait qu'il est déconnecté de son réseau ou que c'est un utilisateur nomade.

    Cela veut dire que, d'une manière ou d'une autre, on aura toujours affaire au générateur au final qui se chargera d'attribuer une clé unique à ces enregistrements.

    Citation Envoyé par o_live Voir le message
    Le composant IBQuery a une propriété GeneratorField qui liste les générateurs pressent dans la base est ce que cela ne pourrait pas être une solution ?
    comment l'utiliser ?
    IBQuery c'est pour interbase et comme il est fort probable qu'interbase et firebird pourraient prendre des chemins différents. Pour être tranquille, il est préférable d'utiliser des compos gratuit entierements compatible Firebird comme par exemple UIB.

    Pour les générateur dans GeneratorField. Cela vous a été suggérer de gérer vos générateurs au niveau du serveur à l'aide de triggers.

    Citation Envoyé par o_live Voir le message
    auriez vous un petit exemple sur une table ?
    -créer le trigger BeforeInsert avec son générateur
    -créer la PS avec
    Code SQL : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
     Insert Into Personne (PRENOM,NOM) VALUE (:C1,:C2) RETURNING PERS INTO :ID; 
    Suspend;
    (ID = paramètre de sortie dans la PS)
    -démarrage de la transaction
    -parcourir le cds maitre avec test if cdsPersonne.UpdateStatus=usInserted then exécutez votre PS avec la query dans laquelle vous récupérez l'ID en fin d'exécution
    -parcours le cds detail avec un test if dsTelephone.UpdateStatus=usInserted then exécutez votre PS en renseignant PERS de la table Telephone avec l'ID récupérer précédemment
    -Commit de la transaction
    -cdsPerson.MergeLog
    -cdsTelephone.MergeLog (effacement du journal des modifications pour eviter de renvoyer même données.)
    -rafraichissement par un select.

    Ne pas invoquer d'applyupdate ni de coder dans le TDataSetProvider.
    Il n'y aura aucun problème d'accès concurrentiel et......, transactionnel multi-poste.

    Bien entendu, le CDS est fourni pour être déployer dans des applications multi-tiers mais dans votre cas il peut-être utiliser dans un environnement 2-tiers avec cette façon de faire décrite ci-dessus.Car, dans la réalité, son utilisation est tout autre et est encore plus sophistiquer.
    Si quelqu'un t'a offensé, ne cherche pas à te venger; assieds-toi au bord de la rivière et, bientôt, tu verras passer son cadavre.

    Lao Tseu - un sage chinois

    Celui qui lutte contre les monstres doit veiller à ne pas le devenir lui-même.
    Et quand ton regard pénètre longtemps au fond d'un abîme, l'abîme, lui aussi, pénètre en toi.

    Friedrich Nietzsche - Par délà le bien et le mal

Discussions similaires

  1. Réponses: 3
    Dernier message: 29/06/2008, 18h56
  2. [EJBQL] [EJB3] Gestion des clés etrangères
    Par niouma dans le forum Java EE
    Réponses: 1
    Dernier message: 20/06/2008, 09h47
  3. [Report] Gestion des titres non numérotés + mise en page
    Par Olivier_ dans le forum Mise en forme
    Réponses: 4
    Dernier message: 08/06/2007, 10h12
  4. [Delphi - Firebird] Gestion des transactions
    Par Lili21 dans le forum Connexion aux bases de données
    Réponses: 9
    Dernier message: 20/04/2007, 17h34
  5. Gestion des clés étrangères
    Par Gonelle dans le forum HyperFileSQL
    Réponses: 1
    Dernier message: 06/07/2006, 10h48

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo