| auteur : Gabrielly | La prise en charge des bases de données par les MFC fait appel à SQL pour formuler les
requêtes et opérations exécutées sur les tables de base de données, par l'intermédiaire d'un ensemble de classes spécialisées.
Lorsque nous faisons appel à SQL dans un programme MFC, nous n'avons généralement pas besoin d'écrire d'instructions SQL complètes, car le
squelette ou canevas de base de l'application se charge de construire une instruction et de la fournir au moteur de base de données utilisé.
|
| auteur : Gabrielly | Les requêtes SQL sont transmises sous forme de chaînes. Si le nom d'une table comporte des
espaces comme Product ID, la syntaxe SQL exige qu'il soit placé entre guillements comme "Product ID".
Ex1: SELECT "Product ID" FROM Products
Or en C++ les guillements délimitent les chaînes de caractères ce qui prête confusion
si nous encadrons le nom des objets de base de données (lignes ou colonnes) par des
guillements.
C'est pourquoi, lorsque nous référençons dans l'environnement Visual C++ une table de base
de données ou un nom de colonne comprenant des espaces, nous devons remplacer les
guillemets par les crochets. En l'ocurrence, écrivons [Product ID].
Ex2: SELECT [Product ID] FROM Products
|
| auteur : Gabrielly | Les MFC abordent les base de données selon deux approches chacune d'elle utilisant son propre
jeu de classes MFC. Il s'agit de DAO et ODBC. Mais il y en a de plus récentes comme OLE DB et ADO.NET.
La première approche DAO (Data Acess Objects) propose une interface avec le moteur de base
de données Jet, élément logiciel généralisé qui permet de stocker des données dans divers
systèmes de gestion de base de données et de les extraires.
Jet est le moteur utilisé par le SGBD Microsoft Access. Chaque fois que vous manipulez une base de données sous Access,
c'est en réalité Jet qui fournit l'essentiel du travail.
Jet est optimisé pour l'accès direct aux fichiers de base de données Access (.mdb), mais il vous permet également de
vous connecter à toutes base de données prennant en charge ODBC.
Outre Microsoft Acess, Jet vous permet d'accéder à des bases de données comme
Oracle, dBase 5, Btrieve 6.0 et FoxPro 2.6 et d'autres encore.
La seconde approche est spécifique à ODBC (Open DataBase Connectivity).
Vous pouvez manier des bases de données de tout format pour lesquelles vous
possédez le pilote ODBC approprié et même pour les fichiers .mdb d'Access.
|
| auteur : Gabrielly | ODBC est une interface, indépendante du système, avec un environnement de base de données qui exige
un pilote ODBC pour chaque système de base de données à partir duquel vous voulez gérer les données.
ODBC définit un ensemble d'appels de fonctions associées à des opérations de base de données,
indépendants du système.
Vous pouvez utiliser une base de données avec ODBC seulement si vous disposez de la DLL qui contient le pilote compatible avec le format de fichier de cette base de données.
L'objectif du pilote est d'assurer l'interface entre le jeu d'appels indépendants du système, pour des opérations de base de données, utilisés dans votre programme, et les spécificités de la mise en oeuvre d'une base de données particulière.
|
| auteur : Gabrielly | ODBC fait appel aux cinq classes suivantes:
CDatabase : Un objet de cette classe représente une connexion à votre base de données, que vous devez établir
avant d'effectuer des opérations sur la base de données. Aucune classe d'espace de travail n'est
utilisée avec une base de données ODBC.
CRecordset : Un objet d'une classe dérivée de cette classe représente le résultat d'une opération SQL SELECT,
Il s'agit du concept mentionné lors de la description de la classe CDaoRecordset
CRecordView : Un objet d'une classe dérivée de cette classe permet d'afficher les informations en cours d'un jeu de
lignes associé. Il s'agit du concept mentionné lors de la description de la classe CDaoRecordView
CFieldExchange : Cette classe permet l'échange de données entre la base de données et un jeu de lignes, comme
pour les bases de données DAO.
CDBException : Les objets de cette classe constituent des exceptions survenant dans les opérations de base de
données ODBC.
Les classes ODBC ressemblent étrangement à un sous-ensemble des classes DAO, ce qui s'explique dans la mesure où l'interface
qu'elles proposent est identique à celle des classes DAO équivalentes.
Elles diffèrent par leur processus sous-jacent d'accès à la base de données.
|
| auteur : Gabrielly | Avant de pouvoir utiliser une base de données via ODBC, vous devez l'inscrire dans la base de registres.
Pour ce faire cliquer sur le panneaux de configuration, sélectionner Outils d'administration et enfin Source de données ODBC.
Une fois que la boîte s'affiche à l'écran sélectionner l'onglet DSN utilisateur ( Source de données utilisateur).
Cliquez sur Ajouter... pour ajouter une source de données.
Une autre boîte s'affiche, et vous sélectionner le pilote soit ODBC Microsoft Acess Driver (*.mdb) (c'est un exemple).
Cliquez sur Terminer.
Une autre boite s'affiche à nouveau, et vous tapez le nom de votre source de données. Ce nom sera utilisé pour identifier
la base de données lors de la création de votre application par l'intermédiaire de AppWizard (VC++ 6.0).
Cliquez sur le bouton Sélectionner... pour accéder à la dernière boîte de dialogue où vous pouvez sélectionner le fichier
qui représente votre base de donnée.( par exemple un fichier *.mdb)
Enfin vous cliquez sur tous les trois boutons OK et l'enregistrement est terminer.
Vérifier à l'aide de l'explorateur Window que votre fichier(base de données) n'est pas en lecture seule.
|
| auteur : Gabrielly | On exécute l'assistant AppWizard avec l'interface SDI ou MDI qui présente à sa seconde étape
des options comme la prise en charge de fichiers avec les options d'affichage de base de données.
La prise en charge des fichiers fait référence à la sérialisation du document, qui n'est généralement pas nécessaire puisque les jeux de lignes de votre application se charge de toutes entrées-sorties nécessaire sur une base de données.
Quant au document, il est en quelque sorte accessoire pour les opérations sur une base de données, car ce sont les objets jeu de lignes et vue de lignes qui se chargent de l'essentiel.
Le rôle principal du document consiste à stocker les jeux de lignes c'est à dire les CRecordset.
On choisit par exemple l'option "Database view without file support" on clique sur Data Source...
et on indique la base de données que l'application va utiliser. L'option ODBC est déjà sélectionnée.
AppWizard fournit automatiquement une classe jeu de lignes et vue de lignes.
L'option "Table" s'applique seulement si vous sélectionnez DAO.
ODBC offre le choix entre deux options "Snapshot" et "Dynaset".
Qu'est-ce :?:
|
| auteur : Gabrielly | Ces mots "jeu de lignes" comme vous avez pu le constater reviennent très souvent.
C'est un objet de la classe dérivée de CRecordset
Voici un exemple de cette classe qui s'appelle "CDBSet" appartenant à un projet SDI nommé "DB".
class CDBSet : public CRecordset
{
public :
CDBSet (CDatabase* pDatabase = NULL );
DECLARE_DYNAMIC (CDBSet)
longm_ProductID;
CStringm_ProductName;
longm_SupplierID;
longm_CategoryID;
CStringm_QuantityPerUnit;
CStringm_UnitPrice;
intm_UnitsInStock;
CStringm_UnitsOnOrder;
intm_ReorderLevel;
BOOLm_Discontinued;
public :
virtual CString GetDefaultConnect ();
virtual CString GetDefaultSQL ();
virtual void DoFieldExchange (CFieldExchange* pFX);
# ifdef _DEBUG
virtual void AssertValid () const ;
virtual void Dump (CDumpContext& dc) const ;
# endif
} ;
|
Nous savons qu'une table de base de données est constituée des colonnes qui représentent les champs de la table et des lignes qui représentent les enregistrements.
Très souvent et la plupart des cas les colonnes sont beaucoup moins nombreuses que les lignes.
Par exemple une table peut avoir une dizaine de colonnes mais des milliers voir même des centaines de milliers de lignes et d'ailleurs jusqu'au million de lignes et même plus.
Dans un programme MFC avec les bases de données, le programmeur ne voit pas dans son code toutes ces lignes simultanément mais il les voit une par une car son champ de vision de la table est réduit à une ligne de cette table.
Et bien, c'est le cas du jeu de ligne qui représente un et un seul enregistrement de la table.
Cela lui vaut bien son nom de "recordset" celui qui définit un enregistrement.
Voici les champs d'une table attaché au recordset CBDSet:
longm_ProductID;
CStringm_ProductName;
longm_SupplierID;
longm_CategoryID;
CStringm_QuantityPerUnit;
CStringm_UnitPrice;
intm_UnitsInStock;
CStringm_UnitsOnOrder;
intm_ReorderLevel;
BOOLm_Discontinued;
|
NB: La table je l'ai crée sur access et s'appelle Product.
Par conséquent si le programmeur veut voir les autres lignes, il doit se déplacer de ligne en ligne dans la table en utilisant les fonctions membres héritées de CRecordset.
Le jeu de ligne représente un enregistrement de la table, et c'est par lui que se fait l'opération SQL SELECT
Toutes les opérations SQL que l'on fait sur SELECT s'appliquent sur le jeu de ligne.
Si je veux sélectionner quelques champs de ma table, ou je veux trier, ou alors placer des conditions de sélection dans la clause WHERE, c'est le jeu de ligne que j'utilise.
C'est la classe MFC ODBC préparée à cela.
Voir les classes ODBC dans les Q/R plus haut.
|
| auteur : Gabrielly | NB: Pour mieux comprendre les différences entre snapshot et dynaset je vous prie de vous reporter à la question Qu'est-ce qu'un jeu de lignes?.
Votre objet jeu de ligne snapshot vous fournit le résultat d'une opération SELECT sur la base de données.
Dans le cas d'un jeu de lignes snapshot, la requête est exécutée une seule fois et le résultat est stocké dans la mémoire.
Votre objet jeu de ligne peut ensuite rendre disponible n'importe quelle ligne de la table issue de la requête.
Par conséquent un snapshot est principalement statique par nature.
Aucune modification apportée à la base de données à la suite d'une mise à jour par d'autres utilisateurs n'est répercutée dans les données que vous obtenez
à l'aide de votre jeu de lignes snapshot.
Si vous voulez voir ces modifications, vous devez réexécuter l'instruction SELECT.
Le choix entre DAO et ODBC détermine une autre fonctionnalité des jeux de ligne snaphot.
Votre programme ne peut pas modifier un jeu de ligne snapshot DAO: il est en lecture seule.
Un snapshot ODBC peut être en lecture seule ou modifiable .
Un snapshot modifiable écrit toutes les modifications apportées à la table dans la base de données sous-jacente, et votre programme peut détecter les changements. D'autres programmes comportant un snapshot de la base de données ne détectent pas cependant les modifications tant qu'ils n'interrogent pas de nouveau la base.
|
| auteur : Gabrielly | NB: Pour mieux comprendre les différences entre snapshot et dynaset je vous prie de vous reporter à la question Qu'est-ce qu'un jeu de lignes?.
Votre objet jeu de lignes dynaset rafraîchit automatiquement la ligne en cours de la base de données lorsque que vous passez d'une ligne à une autre de la table créée par la requête du jeu de lignes.
En conséquence, la ligne disponible dans le jeu de lignes reflète l'état de la base de données lors de l'accès à la ligne, non lors de la première ouverture du jeu
de lignes.
Le rafraîchissement survient uniquement lorsque votre objet jeu de lignes accède à la ligne.
Si un autre utilisateur modifie les données de la ligne en cours, ce changement est pris en compte dans votre objet jeu de lignes uniquement lorsque vous passez à une autre ligne puis revenez vers la ligne initiale.
Un jeu de ligne dynaset génère le contenu de chaque ligne de façon dynamique, au moyen d'un index désignant les tables de base de données concernées.
|
| auteur : Gabrielly | Après avoir lu les définitions sur un jeu de ligne snapshot et dynaset. Nous pouvons affirmer ceci:
Pour un jeu de ligne snapshot, l'utilisateur de la base de données se rendra compte qu'une table a été modifiée lorsqu'il reprend son instruction SELECT sur la table à l'aide de son snapshot c'est à dire son recordset.
Ainsi entre la première ouverture et la reprise de l'instruction SELECT, d'autres utilisateurs peuvent modifier la table à l'insu du premier
utilisateur et donc tant qu'il n'a pas repris l'instruction SELECT il croit toujours que les données de sa table sont saines alors qu'elles sont déjà altérées.
Pour un jeu de ligne dynaset, l'utilisateur se rendra compte de toutes modifications sur une table de la base de données lorsqu'il se déplace simplement d'une ligne à une autre de la table sans qu'il n'ait besoin de réexécuter l'instruction SELECT car le dynaset met automatiquement à jour dès qu'il accède à une ligne de la table.
C'est pourquoi il est dit dynamique et l'utilisateur peut se rendre compte des changements qui se produisent dans son programme un peu plus tôt que celui qui utilise un snapshot.
Par conséquent un snapshot convient pour les tables en lecture seule où aucun utilisateur ne modifie la table ou convient alors dans les situations où l'utilisateur est seul à manipuler la table c'est à dire dans un environnement non client/serveur.
Et un dynaset convient pour les tables qui acceptent les mise à jours et dans le cas où plusieurs utilisateurs peuvent interroger la base sur une même table cas d'un environnement client/serveur.
|
| auteur : Gabrielly | Pour les jeux de lignes snapshot on peut y associer plusieurs tables. Cela équivaut à une opération de jointure des tables.
Tandis que pour les jeux de lignes dynaset on y associe qu'une seule table car les classes de base de données des MFC ne prennent pas en charge la mise à jour de jeux de lignes impliquant la liaison de plusieurs tables.
Si nous revenons à la configuration d'un projet ODBC, en cliquant sur OK, l'assistant affiche toutes les tables de la base de données.
On peut faire une sélection unique ou multiple dans le cas d'un snapshot mais une sélection unique dans le cas d'un dynaset.
Pour le CDBSet j'ai réalisé une sélection unique et j'ai choisit un jeu de ligne snapshot.
On peut le voir dans le constructeur
CDBSet:: CDBSet (CDatabase* pdb)
: CRecordset (pdb)
{
m_ProductID = 0 ;
m_ProductName = _T (" " );
m_SupplierID = 0 ;
m_CategoryID = 0 ;
m_QuantityPerUnit = _T (" " );
m_UnitPrice = _T (" " );
m_UnitsInStock = 0 ;
m_UnitsOnOrder = _T (" " );
m_ReorderLevel = 0 ;
m_Discontinued = FALSE;
m_nFields = 10 ;
m_nDefaultType = snapshot;
}
|
|
| auteur : Gabrielly | Voici la déclaration de cette fonction dans CDBSet
virtual CString GetDefaultConnect ();
|
Elle est virtuelle!!!
Voici son implémentation
CString CDBSet:: GetDefaultConnect ()
{
return _T (" ODBC;DSN=Gestion de stock " );
}
|
Elle est courte!!!
Rappelez vous de la classe document ici chez moi CDBDoc
Voici la déclaration dans CDBDoc
# include "DBSet.h"
class CDBDoc : public CDocument
{
protected :
CDBDoc ();
DECLARE_DYNCREATE (CDBDoc)
public :
CDBSet m_dBSet;
} ;
|
Lorsque le programme démarre, l'objet document est construit et l'objet m_dBSet aussi.
Voici la déclaration de son construteur
CDBSet (CDatabase* pDatabase = NULL );
|
Et donc si le jeu de ligne m_dBSet est construit avec un argument NULL dans son constructeur, ce qui est le cas,
alors la charpente d'application crée pour nous le CDatabase qui représente notre connexion à notre base de données et appelle la fonction virtuelle GetDefaultConnect() qui retourne une chaîne intéressante.
Ce qui est encore plus intéressant c'est qu'un CRecordset est toujours associé à un CDatabase car il représente une connexion à la base de données.
NB: Vous n'avez pas à appeler GetDefaultConnect, c'est le canevas de l'application qui le fait pour vous.
Vous n'avez qu'à définir la chaîne.
soit "ODBC;DSN=Gestion de stock" où ODBC représente l'interface, et Gestion de stock le DSN que j'ai fournit à l'administrateur ODBC dans Panneaux de configuration.
C'est au moyen de cette chaîne que l'on se connecte à notre base de données car la charpente s'en sert comme identification de notre base de données.
Si on veux utiliser un ID utilisateur et un password on écrit
CString CDBSet:: GetDefaultConnect ()
{
return _T (" ODBC;DSN=Gestion de stock;UID=Gabrielly;PWD=Angel " );
}
|
Si l'on veut ouvrir une boite de dialogue pour se connecter on écrit;
Voir aussi la Q/R "Comment récupérér une connexion à une base de données."
|
| auteur : Gabrielly | Cette fonction retourne un CString qui représente la requête SELECT par défaut appliqué au jeu de ligne.
Déclaration dans CDBSet
virtual CString GetDefaultSQL ();
|
Implémentation:
CString CDBSet:: GetDefaultSQL ()
{
return _T (" [Product] " );
}
|
N'oublier pas les MFC transmettent les requêtes sous forme de chaines et fabrique la syntaxe SQL correcte
qu'il fournit au moteur de base de données.
Ce qui montre que vous n'avez pas besoin d'écrire une instruction SQL complète car le canevas de l'application s'en charge.
Si le jeu de ligne est associer à deux tables on a;
CString CMyRecordset:: GetDefaultSQL ()
{
return _T (" [Table1], [Table2] " );
}
|
Comme résultat, c'est une jointure des deux tables
NB: Ici aussi vous n'avez pas à appeler GetDefaultSQL, c'est le travail de la charpente d'application.
|
| auteur : Gabrielly | Le role de cette fonction est d'assurer l'échange de données entre la base de données et le jeu de lignes
Voici un exemple de code:
void CDBSet:: DoFieldExchange (CFieldExchange* pFX)
{
pFX- > SetFieldType (CFieldExchange:: outputColumn);
RFX_Long (pFX, _T (" [ProductID] " ), m_ProductID);
RFX_Text (pFX, _T (" [ProductName] " ), m_ProductName);
RFX_Long (pFX, _T (" [SupplierID] " ), m_SupplierID);
RFX_Long (pFX, _T (" [CategoryID] " ), m_CategoryID);
RFX_Text (pFX, _T (" [QuantityPerUnit] " ), m_QuantityPerUnit);
RFX_Text (pFX, _T (" [UnitPrice] " ), m_UnitPrice);
RFX_Int (pFX, _T (" [UnitsInStock] " ), m_UnitsInStock);
RFX_Text (pFX, _T (" [UnitsOnOrder] " ), m_UnitsOnOrder);
RFX_Int (pFX, _T (" [ReorderLevel] " ), m_ReorderLevel);
RFX_Bool (pFX, _T (" [Discontinued] " ), m_Discontinued);
}
|
Elle fonctionne exactemement de la même manière que DoDataExchange avec les boites de dialogues et les vues.
NB: Là aussi c'est la charpente qui fait automatiquement le stockage et l'extraction de données de la base de données.
Le mode CFieldExchange::outputColumn indique que les données doivent être échangées entre la colonne de la base de données
et l'argument correspondant précisé dans chacun des appels suivants de fonctions RFX_()
|
| auteur : Gabrielly | C'est un objet de la classe dérivée de CRecordView. En d'autres termes il s'agit d'un objet représentant une sorte
de formulaire et d'ailleurs CRecordView dérive de CFormView.
Voici un exemple de cette classe qui s'appelle "CDBView" appartenant à un projet SDI nommé "DB".
class CDBSet;
class CDBView : public CRecordView
{
protected :
CDBView ();
DECLARE_DYNCREATE (CDBView)
public :
enum { IDD = IDD_DB_FORM } ;
CDBSet* m_pSet;
public :
CDBDoc* GetDocument ();
public :
public :
virtual CRecordset* OnGetRecordset ();
virtual BOOL PreCreateWindow (CREATESTRUCT& cs);
protected :
virtual void DoDataExchange (CDataExchange* pDX);
virtual void OnInitialUpdate ();
virtual BOOL OnPreparePrinting (CPrintInfo* pInfo);
virtual void OnBeginPrinting (CDC* pDC, CPrintInfo* pInfo);
virtual void OnEndPrinting (CDC* pDC, CPrintInfo* pInfo);
public :
virtual ~ CDBView ();
# ifdef _DEBUG
virtual void AssertValid () const ;
virtual void Dump (CDumpContext& dc) const ;
# endif
protected :
protected :
DECLARE_MESSAGE_MAP ()
} ;
|
L'objectif d'un objet vue de ligne consiste à afficher les informations d'un objet jeu de ligne dans
la fenêtre de l'application.
Autrement dit il affiche les informations de la table enregistrement par enregistrement car il les récupère du recordset et donc ligne par ligne.
Ce qui est à remarquer est qu'à un objet vue de lignes, on associe toujours un objet jeu de ligne et bien sur le formulaire d'ID IDD_DB_FORM.
class CDBSet;
class CDBView : public CRecordView
{
protected :
CDBView ();
DECLARE_DYNCREATE (CDBView)
public :
enum { IDD = IDD_DB_FORM } ;
CDBSet* m_pSet;
} ;
|
m_pSet est le membre de CDBView qui stocke l'adresse de l'objet jeu de ligne CDBSet.
La charpente fournit toujours ce pointeur pour associer une vue de lignes à un jeu de lignes.
Autrement dit sans jeu de lignes, la vue de lignes est sans intérêt.
|
| auteur : Gabrielly | D'abord il faut placer les contrôles sur le formulaire. Soient des contrôles static et des edits.
Et à l'aide de classwizard on fait l'associer directement avec les membres de l'objet jeu de lignes m_pSet de CDBView en écrivant m_pSet->
Voici DodataExchange après associations
void CDBView:: DoDataExchange (CDataExchange* pDX)
{
CRecordView:: DoDataExchange (pDX);
DDX_FieldText (pDX, IDC_CATEGORYID, m_pSet- > m_CategoryID, m_pSet);
DDX_FieldText (pDX, IDC_PRODUCTID, m_pSet- > m_ProductID, m_pSet);
DDX_FieldText (pDX, IDC_PRODUCTNAME, m_pSet- > m_ProductName, m_pSet);
DDX_FieldText (pDX, IDC_UNITPRICE, m_pSet- > m_UnitPrice, m_pSet);
DDX_FieldText (pDX, IDC_UNITSINSTOCK, m_pSet- > m_UnitsInStock, m_pSet);
DDX_FieldText (pDX, IDC_UNITSONORDER, m_pSet- > m_UnitsOnOrder, m_pSet);
}
|
Notre vue de lignes est prêt et est directement connecté aux champs de la table Product par le
CDBSet.
|
| auteur : Gabrielly | 1. A la source nous avons la base de données, avec une table Product.
2. Ensuite le jeu de lignes CDBSet qui est positionné sur une ligne de la table Product et est déclaré dans CDBDoc
3. A l'aide de CDBSet::DoFieldExchange les échanges entre le jeu de lignes et la table sont assurés.
4. A l'autre extrémité j'ai une vue de lignes CDBView qui contient un pointeur m_pSet sur CDBSet.
5. A l'aide de CDBView::DoDataExchange les échanges entre les champs de la table Product par CDBSet
et les contrôles de CDBView sont assurés.
6. Et avec CDBView::OnInitialUpdate les données sont affichées.
|
| auteur : Gabrielly | Pour associer l'objet vue de lignes CDBOrderView au jeu de lignes CDBOrderSet,
il faut d'abord créer une ressource boîte de dialogue comme IDD_ORDERS_FORM avec
les options de style Child et celui de la bordure None (pour notre application SDI DB).
A l'aide de ClassWizard on crée la classe qui dérive de CRecordView. En cliquant sur OK,
l'assistant vous demande de choisir l'objet jeu de lignes à associer à la vue.
Cette boîte doit vous proposer CDBOrderSet; à défaut sélectionnez cette option dans la liste
et cliquez sur le bouton OK.
Vérifiez le destructeur de la classe CDBOrderView, que ClassWizard a mis en oeuvre au
moyen du code suivant:
CDBOrderView:: ~ CDBOrderView ()
{
}
|
Le membre m_pSet désigne un pointeur sur CDBOrderSet qui est déclaré dans le document
et donc nous pouvons supprimer sa destruction dans la vue comme nous l'avons fait.
|
| auteur : Gabrielly | Voici un code très court de CDBView::OnInitialUpdate pour notre projet SDI nommé DB connecté à la table Product
void CDBView:: OnInitialUpdate ()
{
m_pSet = & GetDocument ()- > m_dBSet;
CRecordView:: OnInitialUpdate ();
GetParentFrame ()- > RecalcLayout ();
ResizeParentToFit ();
}
|
1. A partir du document on récupère le jeu de lignes déjà construit et connecté à la base de données (voir Q/R plus haut)
2. On l'associe au jeu de lignes par son membre m_pSet
3. On appelle CRecordView::OnInitialUpdate() qui ouvre le jeu de lignes pour transférer les données de la base vers le jeu
4. On redimensionne le formulaire
A ce niveau on peut tester l'application. Vérifier que votre table a des données une fois créée.
|
| auteur : Gabrielly | Dans le contexte MFC/ODBC;
Pour trier sur un champ d'une table, il suffit de définir le membre CRecordset::m_strSort
comme une chaîne contenant le nom de colonne sur lequel nous voulons effectuer le tri.
void CDBView:: OnInitialUpdate ()
{
m_pSet = & GetDocument ()- > m_dBSet;
m_pSet- > m_strSort = " [ProductID] " ;
CRecordView:: OnInitialUpdate ();
GetParentFrame ()- > RecalcLayout ();
ResizeParentToFit ();
}
|
Dans le jeu de lignes, nous avons attribué à m_strSort le nom de la colonne ProductID.
Les crochets sont utiles même si un nom est dépourvu d'espace, car ils différencient les chaînes
contenant ces noms d'autres chaînes; pour vous permettre de sélectionner immédiatement le nom
de colonnes.
Pour effectuer le tri sur plusieurs colonnes, indiquez le nom de chacune d'elles dans la chaîne,
en les séparant par une virgule.
m_strSort = " [ProductID], [ProductName] "
|
|
| auteur : Gabrielly | Un seul niveau des transactions est pris en charge ; vous ne pouvez pas imbriquer les transactions.
Pour exécuter une transaction dans un jeu d'enregistrements
Appelez la fonction membre BeginTrans de l'objet CDatabase.
Appelez aussi souvent que nécessaire les fonctions membres AddNew/Update, Edit/Update et Delete d'un ou de plusieurs objets de jeu d'enregistrements
de la même base de données.
Enfin, appelez la fonction membre CommitTrans de l'objet CDatabase.
Si une erreur se produit pendant l'une des mises à jour ou que vous décidiez d'annuler les modifications, appelez la fonction membre Rollback.
Voici un exemple qui ilustre deux jeux d'enregistrements pour supprimer l'inscription d'un étudiant d'une base de données gérant les inscriptions d'une école.
L'étudiant est ainsi retiré de tous les cours pour lesquels il était inscrit.
Les appels Delete doivent réussir dans les deux jeux d'enregistrements, ce qui implique l'utilisation d'une transaction.
Cet exemple suppose l'existence de m_dbStudentReg, variable membre de type CDatabase déjà connectée à la source de données,
et des classes de jeux d'enregistrements CEnrollmentSet et CStudentSet. La variable strStudentID contient une valeur fournie par l'utilisateur.
BOOL CEnrollDoc:: RemoveStudent ( CString strStudentID )
{
if ( ! m_dbStudentReg.BeginTrans ( ) )
return FALSE;
CEnrollmentSet rsEnrollmentSet (& m_dbStudentReg);
rsEnrollmentSet.m_strFilter = " StudentID = " + strStudentID;
if ( ! rsEnrollmentSet.Open (CRecordset:: dynaset) )
return FALSE;
CStudentSet rsStudentSet (& m_dbStudentReg);
rsStudentSet.m_strFilter = " StudentID = " + strStudentID;
if ( ! rsStudentSet.Open (CRecordset:: dynaset) )
return FALSE;
TRY
{
while ( ! rsEnrollmentSet.IsEOF ( ) )
{
rsEnrollmentSet.Delete ( );
rsEnrollmentSet.MoveNext ( );
}
rsStudentSet.Delete ( );
m_dbStudentReg.CommitTrans ();
}
CATCH_ALL (e)
{
m_dbStudentReg.Rollback ();
return FALSE;
}
END_CATCH_ALL
rsEnrollmentSet.Close ();
rsStudentSet.Close ();
return TRUE;
}
|
Remarque Le fait d'appeler une nouvelle fois BeginTrans sans appeler CommitTrans ou Rollback constitue une erreur.
Car tentative d'imbrication des transaction.
|
| auteur : Gabrielly | Une transaction constitue une façon de regrouper une série de mises à jour dans une source de données de manière à ce qu'elles soient toutes validées
en même temps, ou qu'aucune ne soit validée si vous annulez la transaction.
Si vous n'utilisez pas une transaction, les modifications apportées à la source de données sont validées automatiquement au lieu d'être validées sur demande.
Tous les pilotes de base de données ODBC prennent en charge les transactions.
Appelez la fonction membre CanTransact de votre objet CDatabase ou CRecordset pour savoir si votre pilote prend en charge les transactions
pour une base de données particulière.
Sachez cependant que CanTransact ne vous indique pas si la source de données assure une prise en charge complète des transactions.
Vous devez également appeler CDatabase::GetCursorCommitBehavior et CDatabase::GetCursorRollbackBehavior après CommitTrans
et Rollback pour vérifier l'effet de la transaction sur l'objet CRecordset ouvert.
Les appels adressés aux fonctions membres AddNew et Edit d'un objet CRecordset affectent immédiatement la source de données
lorsque vous appelez Update.
Les appels Delete entrent également en vigueur immédiatement.
En revanche, vous pouvez utiliser une transaction composée de plusieurs appels à AddNew, Edit, Update et Delete,
qui sont exécutés mais pas validés tant que vous n'avez pas explicitement appelé CommitTrans.
En établissant une transaction, vous pouvez exécuter une série d'appels de ce type tout en conservant la possibilité de les annuler.
Si une ressource indispensable n'est pas disponible ou que tout autre facteur empêche l'exécution de l'intégralité de la transaction,
vous pouvez annuler cette transaction au lieu de la valider. Dans ce cas, aucune des modifications appartenant à la transaction n'affecte la source de données.
Les transactions sont particulièrement utiles lorsque vous devez mettre à jour plusieurs enregistrements à la fois.
Dans ce cas, vous devez éviter qu'une transaction soit seulement à moitié terminée,
ce qui peut se produire si une exception est générée avant l'exécution de la dernière mise à jour.
Le groupement de ces mises à jour dans une transaction permet une récupération (annulation) après modifications et fait repasser les enregistrements
dans l'état qu'ils avaient avant la transaction.
Un seul niveau des transactions est pris en charge.
Il est impossible d'imbriquer des transactions et une transaction ne peut pas englober plusieurs objets de base de données.
|
| auteur : Gabrielly | Pour lancer une nouvelle requête sur un objet jeu d'enregistrements
Appelez la fonction membre Requery de l'objet.
Autre solution, fermez le jeu d'enregistrements original et réouvrez-le.
Dans les deux cas, le nouveau jeu d'enregistrements représente l'état courant de la source de données.
if ( ! rsStudent.Open ( ) )
return FALSE;
rsStudent.Requery ();
|
|
| auteur : Gabrielly |
C'est en procédant par des verrouillages des enregistrements.
Lorsque vous utilisez un jeu d'enregistrements pour modifier un enregistrement de la source de données,
votre application peut verrouiller l'enregistrement afin qu'aucun autre utilisateur ne puisse le modifier en même temps.
Comme l'état d'un enregistrement modifié simultanément par deux utilisateurs est imprévisible,
il importe que le système puisse garantir que deux utilisateurs ne peuvent pas modifier simultanément un même enregistrement.
Il existe deux types de verrouillage supportés par la classe CRecordset.
-le verrouillage optimiste (mode par défaut)
-le verrouillage pessimiste.
Le verrouillage optimiste verrouille l'enregistrement de la source de données uniquement pendant l'appel de la fonction Update.
Si vous utilisez le verrouillage optimiste dans un environnement multi-utilisateur, l'application doit gérer une condition d'échec de la fonction Update.
Le verrouillage pessimiste verrouille l'enregistrement dès que vous appelez Edit et ne le libère pas tant que vous n'avez pas appelé Update
(les échecs sont signalés via CDBException, et non par la valeur FALSE retournée par Update).
Le verrouillage pessimiste pénalise potentiellement les performances des autres utilisateurs,
car les accès simultanés au même enregistrement doivent attendre la fin complète du processus Update de votre application.
rsStudent.SetLockingMode (CRecordset:: optimistic);
rsStudent.Edit ();
rsStudent.m_strStreet = strNewStreet;
rsStudent.m_strCity = strNewCity;
rsStudent.m_strState = strNewState;
rsStudent.m_strPostalCode = strNewPostalCode;
if ( ! rsStudent.Update () )
{
AfxMessageBox ( " Impossible de mettre à jours. " );
return FALSE;
}
|
rsStudent.SetLockingMode (CRecordset:: pessimistic);
rsStudent.Edit ();
rsStudent.m_strStreet = strNewStreet;
rsStudent.m_strCity = strNewCity;
rsStudent.m_strState = strNewState;
rsStudent.m_strPostalCode = strNewPostalCode;
if ( ! rsStudent.Update () )
{
AfxMessageBox ( " Impossible de mettre à jours. " );
return FALSE;
}
|
En spécifiant CRecordset::pessimistic ou CRecordset::optimistic à la fonction membre SetLockingMode
Le nouveau mode de verrouillage demeure effectif tant que vous ne le modifiez pas à nouveau ou que le jeu d'enregistrements n'est pas fermé.
Remarque: Très peu de pilotes ODBC prennent actuellement en charge le verrouillage pessimiste.
|
| auteur : Gabrielly | Vous pouvez supprimer des enregistrements si la fonction membre CanUpdate retourne une valeur différente de zéro.
Assurez-vous que le jeu d'enregistrements est modifiable.
Accédez à l'enregistrement à mettre à jour.
Appelez la fonction membre Delete de l'objet jeu d'enregistrements.
Delete marque immédiatement l'enregistrement comme supprimé, à la fois dans le jeu d'enregistrements et dans la source de données.
Contrairement à AddNew et Edit, Delete n'appelle pas la fonction Update.
Accédez par défilement à un autre enregistrement.
if ( ! rsStudent.Open ( ) )
return FALSE;
if ( ! rsStudent.CanUpdate () )
return FALSE;
rsStudent.SetAbsolutePosition (3 );
rsStudent.Delete ();
if (rsStudent.IsDelete ())
rsStudent.MoveNext ();
|
|
| auteur : Gabrielly |
Vous pouvez modifier des enregistrements existants si la fonction membre CanUpdate retourne une valeur différente de zéro.
Assurez-vous que le jeu d'enregistrements est modifiable.
Accédez à l'enregistrement à mettre à jour.
Appelez la fonction membre Edit de l'objet jeu d'enregistrements.
Edit prépare le jeu d'enregistrements de telle sorte qu'il fasse office de tampon d'édition.
Tous les membres de données de type champ sont marqués de sorte que le jeu d'enregistrements peut savoir, par la suite, s'ils ont été modifiés.
Les nouvelles valeurs des membres de données de type champ modifiés sont écrites dans la source de données quand vous appelez Update.
Définissez les valeurs des membres de données de type champ du nouvel enregistrement.
Affectez des valeurs aux membres de données de type champ. Les membres auxquels ne sont pas attribuées de valeurs demeurent inchangés.
Appelez la fonction membre Update de l'objet jeu d'enregistrements.
Update termine la modification en écrivant l'enregistrement modifié dans la source de données.
if ( ! rsStudent.Open ( ) )
return FALSE;
if ( ! rsStudent.CanUpdate () )
return FALSE;
rsStudent.SetAbsolutePosition (3 );
rsStudent.Edit ( );
rsStudent.m_strStreet = strNewStreet;
rsStudent.m_strCity = strNewCity;
rsStudent.m_strState = strNewState;
rsStudent.m_strPostalCode = strNewPostalCode;
if ( ! rsStudent.Update () )
{
AfxMessageBox ( " Impossible de mettre à jours. " );
return FALSE;
}
|
Conseil:
Pour annuler un appel de AddNew ou Edit, effectuez simplement un nouvel appel de AddNew ou Edit ou appelez Move
avec le paramètre AFX_MOVE_REFRESH.
Les membres de données sont réinitialisés à leurs valeurs précédentes et vous demeurez en mode Edit ou Add.
Attention:
Lorsque vous vous préparez à modifier un jeu d'enregistrements en appelant Update,
prenez soin que le jeu d'enregistrements comporte toutes les colonnes composant la clé primaire de la table (ou toutes les colonnes d'un index unique de la table).
Dans certains cas, l'infrastructure ne peut utiliser que les colonnes sélectionnées dans le jeu d'enregistrements pour identifier l'enregistrement de la table à modifier.
Sans toutes les colonnes nécessaires, plusieurs enregistrements risquent d'être modifiés dans la table et l'intégrité référentielle de celle-ci peut s'en trouver altérée.
Dans ce cas, l'infrastructure lève une exception quand vous appelez Update.
De préférence placez le code dans les blocs try-catch pour récupérer d'éventuelles exceptions.
|
| auteur : Gabrielly | Vous pouvez ajouter de nouveaux enregistrements à un jeu d'enregistrements si sa fonction membre CanAppend retourne une valeur différente de zéro.
Assurez-vous que des enregistrements peuvent être ajoutés au jeu d'enregistrements.
Appelez la fonction membre AddNew de l'objet jeu d'enregistrements.
AddNew prépare le jeu d'enregistrements de telle sorte qu'il fasse office de tampon d'édition.
Tous les membres de données de type champ sont définis avec la valeur particulière Null et marqués comme non modifiés,
de sorte que seules les valeurs modifiées (« dirty ») sont écrites dans la source de données lorsque vous appelez Update.
Définissez les valeurs des membres de données de type champ du nouvel enregistrement.
Affectez des valeurs aux membres de données de type champ.
Ceux qui ne reçoivent pas de valeurs ne sont pas écrits dans la source de données.
Appelez la fonction membre Update de l'objet jeu d'enregistrements.
Update termine l'ajout en écrivant le nouvel enregistrement dans la source de données.
if ( ! rsStudent.Open ( ) )
return FALSE;
if ( ! rsStudent.CanAppend () )
return FALSE;
rsStudent.AddNew ();
rsStudent.m_strName = strName;
rsStudent.m_strCity = strCity;
rsStudent.m_strStreet = strStreet;
if ( ! rsStudent.Update ( ) )
{
AfxMessageBox ( " Impossible de mettre à jours " );
return FALSE;
}
|
Conseil:
Pour annuler un appel de AddNew ou Edit, effectuez simplement un nouvel appel de AddNew ou Edit ou appelez Move
avec le paramètre AFX_MOVE_REFRESH.
Les membres de données sont réinitialisés à leurs valeurs précédentes et vous demeurez en mode Edit ou Add.
Attention:
Lorsque vous vous préparez à modifier un jeu d'enregistrements en appelant Update,
prenez soin que le jeu d'enregistrements comporte toutes les colonnes composant la clé primaire de la table (ou toutes les colonnes d'un index unique de la table).
Dans certains cas, l'infrastructure ne peut utiliser que les colonnes sélectionnées dans le jeu d'enregistrements pour identifier l'enregistrement de la table à modifier.
Sans toutes les colonnes nécessaires, plusieurs enregistrements risquent d'être modifiés dans la table et l'intégrité référentielle de celle-ci peut s'en trouver altérée.
Dans ce cas, l'infrastructure lève une exception quand vous appelez Update.
De préférence placez le code dans les blocs try-catch pour récupérer d'éventuelles exceptions.
|
| auteur : Gabrielly | En principe, lorsque vous ajoutez, modifiez ou supprimez des enregistrements, le jeu d'enregistrements modifie aussitôt la source de données.
Le tableau suivant résume les options disponibles pour les jeux d'enregistrements, accompagnées des différentes caractéristiques de mise à jour.
Type |
Lecture |
Modification
d'un enregistrement |
suppression
d'un enregistrement |
Ajout
d'un enregistrement |
En lecture Seule |
oui |
non |
non |
non |
Ajout Seul |
oui |
non |
non |
oui |
Pleinement Modifiable |
oui |
oui |
oui |
oui |
Un objet jeu d'enregistrements est modifiable si la source de données l'est et que le jeu d'enregistrements a été ouvert comme étant modifiable.
C'est le cas lorsque le recordset est ouvert avec comme type CRecordset::dynaset.
Son caractère modifiable dépend également de l'instruction SQL que vous utilisez, des capacités du pilote ODBC et
de l'éventuel chargement en mémoire de la bibliothèque de curseurs ODBC.
Il est impossible de modifier une source de données ou un jeu d'enregistrements en lecture seule.
C'est le cas lorsqu'il est ouvert comme type CRecordset::snapshot avec pour option CRecordset::readOnly.
Si le jeu d'enregistrement doit permettre uniquement l'ajout d'enregistrement le recordset peut être ouvert avec l'option CRecordset::appendOnly.
Pour déterminer si le jeu d'enregistrements est modifiable.
Appelez la fonction membre CanUpdate de l'objet jeu d'enregistrements.
CanUpdate retourne une valeur différente de zéro si le jeu d'enregistrements est modifiable.
Par défaut, les jeux d'enregistrement sont entièrement modifiables (vous pouvez effectuer les opérations AddNew, Edit et Delete).
Mais vous pouvez également utiliser l'option appendOnly pour ouvrir des jeux d'enregistrements modifiables.
Un jeu d'enregistrements ouvert de cette façon ne permet que l'ajout de nouveaux enregistrements à l'aide de AddNew.
Il n'est pas possible de modifier ou de supprimer des enregistrements existants.
Vous pouvez tester si un jeu d'enregistrements est ouvert uniquement pour permettre l'ajout en appelant la fonction membre CanAppend.
CanAppend retourne une valeur différente de zéro si le jeu d'enregistrements est entièrement modifiable ou ouvert uniquement pour permettre l'ajout.
if ( ! rsStudentSet.Open () )
return FALSE;
if ( ! rsStudentSet.CanUpdate () )
{
AfxMessageBox ( " Impossible de mettre à jours le recordset. " );
return ;
}
if ( ! rsStudentSet.CanAppend () )
{
AfxMessageBox (" Impossible d'ajouter des enregistrements " );
return ;
}
|
|
| auteur : Gabrielly | Dans la classe du jeu d'enregistrement ou jeu de lignes: les champs et les paramètres
class CStudentSet : public CRecordset
{
CString m_strFirstName;
CString m_strLastName;
long m_StudentID;
CString m_strGradYear;
CString m_strGradYrParam;
long m_StudebtIDParam;
} ;
|
Dans le constructeur: les champs et les paramètres
CStudentSet:: CStudentSet (CDatabase* pdb)
: CRecordset (pdb)
{
m_strFirstName = _T (" " );
m_strLastName = _T (" " );
m_StudentID = 0 ;
m_strGradYear = _T (" " );
m_nFields = 4 ;
m_strGradYrParam = _T (" " );
m_StudentIDParam = 0 ;
m_nParams = 2 ;
}
|
Dans DoFieldExchange: les champs et les paramètres
void CStudentSet:: DoFieldExchange (CFieldExchange* pFX)
{
pFX- > SetFieldType (CFieldExchange:: outputColumn);
RFX_Text (pFX, _T (" [FirstName] " ), m_strFirstName);
RFX_Text (pFX, _T (" [LastName] " ), m_strLastName);
RFX_Long (pFX, _T (" [StudentID] " ), m_StudentID);
RFX_Text (pFX, _T (" [GradYear] " ), m_strGradYear);
pFX- > SetFieldType (CFieldExchange:: param);
RFX_Text (pFX, _T (" [GradYear] " ), m_strGradYrParam);
RFX_Long (pFX, _T (" [StudentID] " ), m_StudentIDParam);
}
|
Dans GetDefaultSQL
CString CStudentSet:: GetDefaultSQL ()
{
return _T (" [Student] " );
}
|
Dans une méthode quelconque...
CDatabase mydb;
if ( ! mydb.OpenEx ( _T ( " ODBC;DSN=MYDATASOURCE;UID=JOES " ))
{
AfxMessageBox (" Impossible d'ouvrir la base de donnée " );
return ;
}
CStudentSet rsStudents (mydb);
rsStudents.m_strFilter = " [GradYear] <= ? AND [StudentID] >= ? AND [StudentID] <= '1000' " ;
rsStudent.m_strSort = " LastName DESC, FirstName DESC " ;
CString strGradYear = GetCurrentAcademicYear ();
long StudentID = GetCurrentStudentID ();
rsStudents.m_strGradYrParam = strGradYear;
rsStudents.m_StudentIDParam = StudentID;
rsStudents.Open ();
if (rsStudents.IsOpen ())
{
}
rsStudents.Close ();
mydb.Close ();
|
La requête complète est celle donnée par GetDefaultSQL(), CRecordsert::m_strFilter et CRecordsert::m_strSort.
|
| auteur : Gabrielly |
Vous pouvez définir une ou plusieurs colonnes à partir desquelles effectuer le tri, et
choisir un ordre de tri ascendant ou descendant (ASC ou DESC, ASC étant la valeur par défaut) pour chaque colonne.
Par exemple, si vous indiquez deux colonnes, les enregistrements sont d'abord triés sur la première colonne,
puis sur la seconde.
La clause SQL ORDER BY définit une instruction de tri.
Quand l'infrastructure ajoute la clause ORDER BY à la requête SQL du jeu d'enregistrements,
la clause contrôle le tri de la sélection.
Vous devez établir un ordre de tri du jeu d'enregistrements après avoir construit l'objet mais avant d'appeler
sa fonction membre Open (ou la fonction membre Requery d'un objet jeu d'enregistrements existant
dont la fonction membre Open a été appelée préalablement).
CStudentSet rsStudent ( NULL );
rsStudent.m_strSort = " LastName DESC, FirstName DESC " ;
rsStudent.Open ();
|
Le jeu d'enregistrements contient tous les étudiants, triés par ordre décroissant (de Z à A) sur le nom, puis sur le prénom.
La requête complète est fournit par GetDefaultSQL() et CRecordset::m_strSort
|
| auteur : Gabrielly | Filtrer un jeu d'enregistrements c'est sélectionner un sous-ensemble particulier au sein des enregistrements disponibles.
Par exemple, considérons le recordset suivant:
class CCourse : public CRecordset
{
public :
CCourse (CDatabase* pDatabase = NULL );
...
CString m_strCourseID;
CString m_strCourseTitle;
CString m_strIDParam;
} ;
|
vous pourriez vouloir ne sélectionner que les sections classe d'un cours donné, comme MATH101.
Un filtre est une condition de recherche définie par le contenu d'une clause SQL WHERE.
Quand l'infrastructure l'ajoute à l'instruction SQL du jeu d'enregistrements, la clause WHERE limite la sélection.
Vous devez établir un filtre d'objet jeu d'enregistrements après avoir construit l'objet mais avant d'appeler
sa fonction membre Open (ou la fonction membre Requery d'un objet jeu d'enregistrements existant dont la fonction
membre Open a été appelée préalablement).
CCourse rsCourseSet ( NULL );
rsCourseSet.m_strFilter = " CourseID = 'MATH101' " ;
rsCourseSet.Open ( CRecordset:: snapshot, NULL , CRecordset:: readOnly );
if ( ! rsCourseSet.IsOpen () )
return ;
while ( ! rsCourseSet.IsEOF ( ) )
{
CString strTitle = rsCourseSet.m_strCourseTitle;
TRACE (" %s\n " , (LPCTSTR) strTitle);
rsCourseSet.MoveNext ( );
}
rsCourseSet.m_strFilter = " CourseID = ? " ;
rsCourseSet.m_strIDParam = " PROGRAMMATION " ;
rsCourseSet.Requery ();
while ( ! rsCourseSet.IsEOF ( ) )
{
CString strTitle = rsCourseSet.m_strCourseTitle;
TRACE (" %s\n " , (LPCTSTR) strTitle);
rsCourseSet.MoveNext ( );
}
|
La requête complète est fournit par GetDefaultSQL() et CRecordset::m_strFilter
|
| auteur : Gabrielly | Lorsque vous naviguez au sein d'un jeu d'enregistrements,
il arrive fréquemment que vous ayez besoin de revenir sur un enregistrement donné.
Deux méthodes, le signet et la position absolue, permettent de répondre à ce besoin.
Signets dans ODBC MFC
Un signet identifie un enregistrement de façon unique.
Lorsque vous naviguez au sein d'un jeu d'enregistrements,
vous ne pouvez pas toujours vous fier à la position absolue d'un enregistrement dans la mesure
où les enregistrements peuvent être supprimés du jeu d'enregistrements.
La solution la plus fiable pour conserver la trace de la position d'un enregistrement consiste à utiliser un signet.
La classe CRecordset propose des fonctions membres pour :
obtenir le signet de l'enregistrement courant, de telle sorte que vous puissiez le sauvegarder
dans une variable (GetBookmark) ;
atteindre rapidement un enregistrement donné en indiquant son signet, préalablement sauvegardé
dans une variable (SetBookmark).
CCustSet rsCustSet ( NULL );
rsCustSet.Open ( CRecordset:: snapshot, NULL , CRecordset:: useBookmarks);
CString strSaveRecord = rsCustSet.m_strData;
CDBVariant varRecordToReturnTo;
if (rsCustSet.CanBookmark ())
rsCustSet.GetBookmark ( varRecordToReturnTo );
if (rsCustSet.CanBookmark ())
rsCustSet.SetBookmark ( varRecordToReturnTo );
if (strSaveRecord = = rsCustSet.m_strData)
{
AfxMessageBox (" j'ai retrouvé ma position " );
}
|
Remarque : Vous devez également vérifier que les signets sont toujours présents après certaines opérations sur les
jeux d'enregistrements.
Par exemple, si vous effectuez une opération Requery (lancement d'une nouvelle requête)
sur un jeu d'enregistrements, il se peut que les signets ne soient plus valides.
Appelez CDatabase::GetBookmarkPersistence pour vérifier si vous pouvez faire appel à SetBookmark en toute sécurité.
Positions absolues dans ODBC MFC
Outre les signets, la classe CRecordset permet de définir l'enregistrement courant en indiquant sa position ordinale.
Cette méthode est appelée positionnement absolu.
Le positionnement absolu n'est pas disponible pour les jeux d'enregistrement en avant seulement (CRecordset::forwardOnly).
Pour déplacer le pointeur de l'enregistrement courant à l'aide du positionnement absolu,
appelez CRecordset::SetAbsolutePosition. Lorsque vous transmettez une valeur à SetAbsolutePosition,
l'enregistrement correspondant à cette position ordinale devient l'enregistrement courant.
long nRows = 3 ;
rsCustSet.SetAbsolutePosition (nRows);
|
|
| auteur : Gabrielly | La classe CRecordset propose les fonctions membres Move pour faire défiler un jeu d'enregistrements.
Ces fonctions déplacent l'enregistrement courant par jeu de lignes.
Si vous avez implémenté l'extraction globale de lignes,
l'opération Move repositionne le jeu d'enregistrements en fonction de la taille du jeu de lignes.
Dans le cas contraire, l'appel de la fonction repositionne le jeu d'enregistrements d'un enregistrement à la fois.
Pour défiler :
vers l'avant d'un enregistrement ou d'un jeu de lignes à la fois, appelez la fonction membre MoveNext ;
vers l'arrière d'un enregistrement ou d'un jeu de lignes à la fois, appelez la fonction membre MovePrev ;
jusqu'au premier enregistrement du jeu d'enregistrements, appelez la fonction membre MoveFirst ;
jusqu'au dernier enregistrement du jeu d'enregistrements ou jusqu'au dernier jeu de lignes, appelez la fonction membre MoveLast ;
de N enregistrements à partir de la position courante, appelez la fonction membre Move.
Pour tester le début ou la fin d'un jeu d'enregistrements
-Vous êtes-vous déplacé au-delà du dernier enregistrement ? Appelez la fonction membre IsEOF.
-Si vous avez atteint le premier enregistrement (déplacement vers l'arrière), Appelez la fonction membre IsBOF.
IsEOF (End Of File) retourne une valeur différente de zéro si le jeu d'enregistrements est positionné au-delà du dernier enregistrement.
IsBOF (Begin Of File) retourne une valeur différente de zéro si le jeu d'enregistrements est positionné avant le premier enregistrement.
Dans les deux cas, il n'y a pas d'enregistrement actuel sur lequel opérer.
Si vous appelez MovePrev alors que IsBOF a déjà la valeur TRUE,
ou si vous appelez MoveNext alors que IsEOF a déjà la valeur TRUE,
l'infrastructure lève une exception CDBException.
Vous pouvez aussi utiliser IsBOF et IsEOF pour vérifier si un jeu d'enregistrements est vide.
CCustSet rsCustSet ( NULL );
rsCustSet.Open ( );
if ( rsCustSet.IsBOF ( ) )
return ;
Cstring strFirstData = rsCustSet.m_strData;
if ( ! rsCustSet.CanScroll () )
return ;
while ( ! rsCustSet.IsEOF ( ) )
{
CString strCurrentData = rsCustSet.m_strData;
rsCustSet.MoveNext ( );
}
rsCustSet.MoveLast ( );
CString strLastData = rsCustSet.m_strData;
while ( ! rsCustSet.IsBOF ( ) )
{
CString strCurrentData = rsCustSet.m_strData;
rsCustSet.MovePrev ( );
}
rsCustSet.MoveFirst ( );
strFirstData = rsCustSet.m_strData;
|
|
| auteur : Gabrielly | De la façon la plus naturelle, il s'agit d'un appel au couple Open()/Close() du recordset.
D'abord on suppose que notre jeu d'enregistrement est construit à partir d'une classe dérivée de CRecordset.
Dans le constructeur, transmettez un pointeur à un objet de CDatabase,
ou la valeur NULL pour utiliser un objet temporaire de base de données que l'infrastructure construira et
ouvrira en fonction de la chaîne de connexion retournée par la fonction membre GetDefaultConnect.
L'objet de CDatabase peut ou non être déjà connecté à une source de données.
Et enfin appelez la fonction membre Open de l'objet.
CStudentSet rsStudent ( NULL );
if (! rsStudent.Open (CRecordset:: snapshot, NULL , CRecordset:: readOnly))
return FALSE;
rsStudent.Close ();
|
Lorsque vous avez fini d'utiliser votre jeu d'enregistrements, vous devez désallouer sa mémoire par sa méthode Close()
par oubli le destructeur le fait pour vous.
|
| auteur : Gabrielly | L'architecture d'un jeu d'enregistrements (jeu de lignes) est définit par ses données membres.
En effet considérons une classe dérivée de CRecordset
class CCourse : public CRecordset
{
public :
CCourse (CDatabase* pDatabase = NULL );
...
CString m_strCourseID;
CString m_strCourseTitle;
CString m_strIDParam;
} ;
|
les membres de données qui composent l'architecture d'un objet jeu d'enregistrements sont:
-les membres de données de type champ
-les membres de données de type paramètre.
Membres de données de type champ:
Ce sont les membres des données les plus important.
Il s'agit de:
-m_strCourseID pour la colonne "CourseID" de la table Course
-m_strCourseTitle pour la colonne "CourseTitle" de la table Course
Pour que les données de la source de données soient fournies à vos membres de données de type champ
la fonction CCourse::DoFieldExchange doit être implémentée et le nombre de champs CRecordset::m_nFields pris en charge
par votre jeu d'enregistrement doit être fixé de préférence dans le constructeur de CCourse
void CCourse:: DoFieldExchange (CFieldExchange* pFX)
{
pFX- > SetFieldType (CFieldExchange:: outputColumn);
RFX_Text (pFX, " CourseID " , m_strCourseID);
RFX_Text (pFX, " CourseTitle " , m_strCourseTitle);
}
CCourse:: CCourse (CDatabase* pdb)
: CRecordset (pdb)
{
m_strCourseID = " " ;
m_strCourseTitle = " " ;
m_nFields = 2 ;
}
|
Membres de données de type paramètre:
Si la classe est « paramétrée », elle possède un ou plusieurs membres de données de type paramètre.
Il s'agit de m_strIDParam.
Une classe paramétrée vous permet de baser une requête de jeu d'enregistrements sur des informations obtenues
ou calculées au moment de l'exécution.
En général, le paramètre vous aide à affiner la sélection, l'objet jeu d'enregistrements pourrait exécuter
l'instruction SQL suivante :
SELECT [CourseID], [CourseTitle] FROM [Course] WHERE [CourseID] = ?
Le « ? » constitue un emplacement réservé pour la valeur de paramètre fournie à l'exécution.
Lorsque vous construisez le jeu d'enregistrements et définissez son membre de données m_strIDParam sur « MATH101 » ,
l'instruction SQL effective du jeu d'enregistrements devient :
SELECT [CourseID], [CourseTitle] FROM [Course] WHERE [CourseID] = MATH101
Pour que ODBC le considère comme paramètre il faut fixer le nombre de paramètres CRecordset::m_nParams et ensuite
ajouter un peu de code dans DoFieldExchange()
void CCourse:: DoFieldExchange (CFieldExchange* pFX)
{
pFX- > SetFieldType (CFieldExchange:: outputColumn);
RFX_Text (pFX, " CourseID " , m_strCourseID);
RFX_Text (pFX, " CourseTitle " , m_strCourseTitle);
pFX- > SetFieldType (CFieldExchange:: param);
RFX_Text (pFX, " CourseID " , m_strIDParam);
}
CCourse:: CCourse (CDatabase* pdb)
: CRecordset (pdb)
{
m_strCourseID = " " ;
m_strCourseTitle = " " ;
m_nFields = 2 ;
m_nParams = 1 ;
}
|
|
| auteur : Gabrielly | Pour vous connecter à une source de données spécifique, votre source de données doit déjà être configurée à
l'aide de l'Administrateur ODBC
Construisez un objet CDatabase.
Appelez sa fonction membre OpenEx ou Open.
CDatabase mydb;
if ( ! mydb.OpenEx ( _T ( " ODBC;DSN=MYDATASOURCE;UID=JOES " ),
CDatabase:: openReadOnly)
{
AfxMessageBox (" Impossible d'ouvrir la base de donnée en lecture seule " );
return ;
}
else
{
AfxMessageBox (" Test d'ouverture réussi " );
mydb.ExecuteSQL (" CREATE TABLE OFFICES (OfficeID TEXT(4) " " ,
OfficeName TEXT (10 ))" );
}
CRecorset r (& mydb);
r.Close ();
mydb.Close ();
if ( ! mydb.OpenEx ( _T ( " ODBC;DSN=SQLServer_Source;Trusted_Connection=Yes; " ),
CDatabase:: forceOdbcDialog)
{
AfxMessageBox (" Impossible d'ouvrir la base de donnée à l'aide de l'authentification Windows NT " );
return ;
}
else
{
AfxMessageBox (" Test d'ouverture réussi " );
}
|
Si la chaîne de connexion ne contient pas suffisamment d'informations alors la boite dialogue de connexion ODBC s'ouvre.
Vous devez fermer tous les jeux de lignes (CRecordset) ouverts avant d'appeler la fonction membre Close de CDatabase.
Dans les jeux de lignes associés à l'objet CDatabase que vous voulez fermer, toutes les instructions AddNew ou Edit et
toutes les transactions en attente sont annulées.
Pour vous déconnecter d'une source de données
Appelez la fonction membre Close de l'objet CDatabase.
Détruisez l'objet, sauf si vous voulez le réutiliser.
Vous pouvez réutiliser un objet CDatabase après une déconnexion, que ce soit pour vous reconnecter à la même
source de données ou pour vous connecter à une source de données différente.
Pour réutiliser un objet CDatabase
Fermez la connexion initiale de l'objet.
Plutôt que de détruire l'objet, appelez à nouveau sa fonction membre OpenEx ou Open.
|
| auteur : Gabrielly | Avant tout établissement de communications avec un système de gestion de base de données (SGBD) pour accéder aux
données, une chaîne de connexion est demandée.
C'est chaîne comprend des informations qui renseignent le pilote ODBC sur la façon d'ouvrir une connexion à la base
de donnée.
Pour des projets générés à l'aide des assistants code, ces derniers fournissent une chaîne de connexion par défaut
qui est retournée par la fonction membre virtuelle CRecordset::GetDefaultConnect.
Cette fonction est appelée par la charpente MFC lors de votre première communication à la base de données.
Vous pouvez redéfinir cette fonction pour imposer aux utilisateurs de votre application un mode d'ouverture à
votre base de données.
CString CMyAppSet:: GetDefaultConnect ()
{
return " ODBC; " ;
}
|
CString CMyAppSet:: GetDefaultConnect ()
{
return " ODBC;DSN=mydb; " ;
}
|
CString CMyAppSet:: GetDefaultConnect ()
{
return " ODBC;DSN=mydb;UID=myuserid; " ;
}
|
CString CMyAppSet:: GetDefaultConnect ()
{
return " ODBC;DSN=mydb;UID=sa;PWD=777; " ;
}
|
CString CApp1Set:: GetDefaultConnect ()
{
return " ODBC;DSN=afx;Trusted_Connection=Yes; " ;
}
|
|
| auteur : Gabrielly | L'Administrateur ODBC est utilisé pour configurer vos sources de données.
Vous pouvez également utiliser l'Administrateur ODBC après l'installation pour ajouter ou supprimer des sources de données.
Lorsque vous créez des applications,
vous pouvez diriger vos utilisateurs vers l'Administrateur ODBC pour leur permettre d'ajouter des sources de données,
ou créer cette fonctionnalité dans votre application en effectuant des appels d'installation ODBC directs.
Vous pouvez utiliser un fichier Excel comme source de données que vous devez configurer afin qu'il soit enregistré et
qu'il apparaisse dans la boîte de dialogue Source de données.
Pour utiliser un fichier Excel comme source de données
Cliquez sur le panneau de configuration, sélectionnez Outils d'administration, et enfin Source de données ODBC.
Sous l'onglet DSN fichier, cliquez sur Ajouter.
Dans la boîte de dialogue Créer une nouvelle source de données, sélectionnez un pilote Excel, puis cliquez sur Suivant.
Cliquez sur Parcourir et sélectionnez le nom du fichier à utiliser comme source de données.
Comme remarque vous devrez peut-être sélectionner Tous les fichiers dans le menu déroulant pour afficher tous les fichiers .xls.
Cliquez sur Suivant, puis sur Terminer.
Dans la boîte de dialogue Installation ODBC pour Microsoft Excel, sélectionnez la version et le classeur de la
base de données.
Pour utiliser votre base de donnée access comme source de données la procédure est semblable.
Cliquez sur le panneau de configuration, sélectionnez Outils d'administration, et enfin Source de données ODBC.
Une fois que la boîte s'affiche à l'écran sélectionner l'onglet DSN utilisateur ( Source de données utilisateur).
Cliquez sur Ajouter... pour ajouter une source de données.
Une autre boîte s'affiche, et vous sélectionnez le pilote ODBC Microsoft Access Driver (*.mdb)
Cliquez sur Terminer.
Une autre boite s'affiche à nouveau, et vous tapez le nom de votre source de données.
Ce nom sera utilisé pour identifier la base de données dans vos projets VC++.
Cliquez sur le bouton Sélectionner... pour accéder à la dernière boîte de dialogue où vous pouvez sélectionner le fichier
qui représente votre base de donnée.(fichier *.mdb)
Enfin vous cliquez sur tous les trois boutons OK et la configuration est terminée.
|
| auteur : Gabrielly | La connexion à une source de données signifie l'établissement de communications avec un système de
gestion de base de données (SGBD) pour accéder aux données. Lorsque vous vous connectez à une source
de données à partir d'une application par le biais d'un pilote ODBC, le pilote établit la connexion pour vous,
localement ou via un réseau.
Vous pouvez vous connecter à toute source de données pour laquelle vous possédez un pilote ODBC.
Les utilisateurs de votre application doivent également disposer du même pilote ODBC pour leur source de données.
La gestion des connexions passe
- Par la configuration de la source de données,
- Par son accès dans un environnement multi-utilisateur
- Par la généralisation d'une chaîne de connexion à la source de données
- Par la façon de se connecter et de se déconnecter à la source de données
- Par la réutilisation de la connexion c'est à dire celle de l'objet CDatabase.
|
| auteur : Gabrielly |
Dans la terminologie de base de données, une source de données représente un ensemble spécifique de données,
les informations nécessaires pour accéder à ces données et l'emplacement de la source de données,
pouvant être décrite par un nom de source de données.
Pour utiliser la classe CDatabase, vous devez configurer la source de données à l'aide de l'Administrateur ODBC.
Une base de données distante s'exécutant sur Microsoft SQL Server au sein d'un réseau ou un fichier
Microsoft Access dans un répertoire local constitue des exemples de sources de données.
Vous pouvez accéder à toute source de données pour laquelle vous possédez un pilote ODBC à partir de votre application.
Une ou plusieurs sources de données peuvent être actives simultanément dans votre application,
Chacune étant représentée par un objet CDatabase.
Plusieurs connexions simultanées à une source de données peuvent également être actives.
Vous pouvez vous connecter à des sources de données distantes ou locales,
selon les pilotes installés et les fonctionnalités de vos pilotes ODBC.
|
| auteur : Gabrielly | C'est en transmettant le membre CRecordset::m_pDatabase d'un jeu de lignes à un autre.
m_pDatabase est un pointeur sur un objet CDatabase qui représente une connexion à une base de données.
Puisque les jeux de lignes CDBset et CDBOrderSet sont attachés aux tables [Product], [Orders] et
[Order Details] d'une même base de données. Il serait une perte de temps de créer à chaque
fois un objet CDatabase pour un CRecordset.
CDBSet (CDatabase* pDatabase = NULL );
CDBOrderSet (CDatabase* pDatabase = NULL );
|
Ainsi il faut utiliser un CDatabase déjà ouvert et le transmettre à un autre CRecordset attaché à la mème base de
données. Mais quand est-ce que CDatabase est construit et que son membre Open() est appelé?
Pour construire le CDatabase nous aurions pu écrire
m_pSet- > m_pDataBase = new CDatabase ();
|
Dans le cas de notre exemple;
Les deux jeux de lignes sont déclarés dans le document CDBDoc. Dans le constructeur
CDBDoc::CDBDoc les constructeurs des jeux sont appelés avec un pointeur NULL (argument par défaut) et les membres
des deux jeux CRecordset::m_pDatabase sont nuls.
Le CDatabase est construit implicitement dans les coulisses des appels des membres
CRecordset::Open ou CRecordView::OnInitialUpdate qui en fin de compte appelle CRecordset::Open.
Si CRecordset::Open se rend compte que CDatabase n'est pas crée, il le construit puis appelle CDatabase::Open
Pour le cas de notre exemple le CDatabase est construit au premier affichage de CDBView par son membre
CDBView::OnInitialUpdate() qui appelle soit CRecordView::OnInitialUpdate ou directement CRecordset::Open.
Mais quant à CDBOrderSet, son membre m_pDatabase est toujours nul d'où la réutilisation.
void CDBOrderView:: OnInitialUpdate ()
{
BeginWaitCursor ();
CDBDoc* pDoc = (CDBDoc* ) GetDocument ();
m_pSet = & pDoc- > m_OrderSet;
m_pSet- > m_pDatabase = pDoc- > m_dBSet.m_pDatabase;
m_pSet- > m_ProductIDParam = pDoc- > m_dBSet.m_ProductID;
m_pSet- > m_strFilter = " [ProductID] = ? AND [Orders].[OrderID] = [Order Details].[OrderID] " ;
m_pSet- > Open ();
if (m_pSet- > IsOpen ())
{
CString strTitle = " Nom de la table : " ;
CString strTable = m_pSet- > GetTableName ();
if (! strTable.IsEmpty ())
strTitle + = _T (" : " ) + strTable;
GetDocument ()- > SetTitle (strTitle);
}
CRecordView:: OnInitialUpdate ();
EndWaitCursor ();
}
|
|
| auteur : Gabrielly |
class CDBOrderSet : public CRecordset
{
public :
CDBOrderSet (CDatabase* pDatabase = NULL );
DECLARE_DYNAMIC (CDBOrderSet)
longm_OrderDetailID;
longm_OrderID;
longm_ProductID;
longm_OrderID2;
long m_ProductIDParam;
|
Dans le constructeur:
CDBOrderSet:: CDBOrderSet (CDatabase* pdb)
: CRecordset (pdb)
{
m_OrderDetailID = 0 ;
m_OrderID = 0 ;
m_ProductID = 0 ;
m_OrderID2 = 0 ;
m_nFields = 29 ;
m_ProductIDParam = 0L ;
m_nParams = 1 ;
m_nDefaultType = snapshot;
}
|
On initialise le paramètre m_ProductIDParam et on fixe le nombre de paramètres à l'aide du membre
CRecordset::m_nParams. Comme vous le constatez on peut utiliser plusieurs paramètres et fixer
ainsi leur nombre à l'aide du membre m_nParams.
Il doit y avoir concordance sinon le fonctionnement est compromis.
Pour identifier la variable m_ProductIDParam de la classe comme un paramètre de substitution dans le filtre
de CDBOrderSet nous devons également ajouter du code au membre de la classe DoFieldExchange()
void CDBOrderSet:: DoFieldExchange (CFieldExchange* pFX)
{
pFX- > SetFieldType (CFieldExchange:: outputColumn);
RFX_Long (pFX, _T (" [OrderDetailID] " ), m_OrderDetailID);
RFX_Long (pFX, _T (" [Order Details].[OrderID] " ), m_OrderID);
RFX_Long (pFX, _T (" [ProductID] " ), m_ProductID);
RFX_Long (pFX, _T (" [Orders].[OrderID] " ), m_OrderID2);
pFX- > SetFieldType (CFieldExchange:: param);
RFX_Long (pFX, _T (" ParamProductID " ), m_ProductIDParam);
}
|
SetFieldType(CFieldExchange::param) définit le mode des appels RFX_() suivants sur param.
Le troisième argument de tous les appels RFX_() suivants est alors interprété comme un paramètre
qui doit remplacer un point d'interrogation dans le filtre du jeu de lignes.
Si vous avez plusieurs paramètres, ils remplacent les points d'interrogation dans la chaîne m_strFilter, de gauche à droite.
Il importe de veiller à ce que les appels RFX_() soient dans le bon ordre.
Lorsque le mode est défini sur param, le second argument de l'appel RFX_() est ignoré, vous pouvez donc le mettre à NULL ici, ou une
autre chaîne si vous voulez.
En quelques mots:
Déclarer les paramètres dans le jeu de lignes.
Initialiser ces paramètres et fixer le nombre de paramètres à l'aide de m_nParams
Utitliser DoFieldExchange pour les identifier comme paramètres de substitutions
|
lien : Comment placer la clause WHERE dans une requête SQL?
|
| auteur : Gabrielly | Dans le context ODBC avec les MFC;
c'est en ajoutant un filtre à la requête par l'attribution d'une chaîne au membre
CRecordset::m_strFilter de l'objet jeu de lignes.
Soit l'instruction suivante:
m_pSet- > m_strFilter = " [ProductID] = ? AND [Orders].[OrderID] = [Order Details].[OrderID] " ;
|
Ici, dans le cas de notre exemple,
m_pSet est le pointeur sur CDBOrderSet de la classe CDBOrderView.
[Orders] et [Order Details] sont deux tables associés au jeu de lignes CDBOrderSet.
Chacune des tables définit son champs [OrderID]. Ici le champ est précédé du nom de sa table.
[ProductID] est un champ de la table [Order Details]
Relativement à cette clause WHERE, il s'agit de produire une table à l'aide de l'opération SQL SELECT
sur l'objet CDBOrderSet pour lequel les champs [OrderID] des deux tables sont égaux et le champ ProductID
de CDBOrderSet correspond à la valeur actuelle du champs ProductID de CDBSet attaché à la table [Product].
Cette valeur courante de ProductID de CDBSet est représentée par le point d'interrogation
Ce symbole ? sera remplacé par un paramètre du filtre qu'il faut définir...
Voir Comment définir un paramètre de filtre dans la clause WHERE d'une requête SQL?
|
lien : Comment définir un paramètre de filtre dans la clause WHERE d'une requête SQL?
|
| auteur : Gabrielly | C'est en ajoutant des jeux de lignes supplémentaires à notre application.
Si notre premier jeu de lignes CDBSet est connecté à la table Product qui fournit les informations sur les produits
nous pouvons insérer une seconde table qui elle gèrera les informations de commandes sur les
produits de la base de données.
En effet, soit CDBOrderSet un autre recordset attaché à deux tables [Orders] et [Order Details]
qui contiennent toutes les commandes correspondant à un produit et soit CDBOrderView
l'objet vue de lignes associé qui affiche les données de CDBOrderSet.
A l'aide de classwizard (VC++ 6.0), on clique sur Add Class... puis sur New.
On saisit le nom de la classe CDBOrderSet et on sélectionne la classe de base CRecordset.
Cliquez ensuite sur OK, Classwizard vous amène à la boite de dialogue permettant de sélectionner la base de données
associées et sous recordset type on conserve le paramétrage snapshot.
Cliquez ensuite sur OK qui nous amène à la sélection des tables de la base de données.
Soient deux tables Orders et Orders Détails préalablement créées qu'on associe à CDBOrderSet,
puis cliquez sur OK pour terminer la procédure.
voici le code généré:
class CDBOrderSet : public CRecordset
{
public :
CDBOrderSet (CDatabase* pDatabase = NULL );
DECLARE_DYNAMIC (CDBOrderSet)
longm_OrderDetailID;
longm_OrderID;
longm_ProductID;
CTimem_DateSold;
doublem_Quantity;
CStringm_UnitPrice;
doublem_Discount;
CStringm_SalePrice;
CStringm_SalesTax;
CStringm_LineTotal;
longm_OrderID2;
longm_CustomerID;
longm_EmployeeID;
CTimem_OrderDate;
CStringm_PurchaseOrderNumber;
CTimem_RequiredByDate;
CTimem_PromisedByDate;
CStringm_ShipName;
CStringm_ShipAddress;
CStringm_ShipCity;
CStringm_ShipState;
CStringm_ShipStateOrProvince;
CStringm_ShipPostalCode;
CStringm_ShipCountry;
CStringm_ShipPhoneNumber;
CTimem_ShipDate;
longm_ShippingMethodID;
CStringm_FreightCharge;
doublem_SalesTaxRate;
|
Deux tables [Orders] et [Order Details] sont associées à notre snapshot.
On peut ne pas avoir besoin de toutes les colonnes. On peut supprimer quelques unes
à l'aide de ClassWizard.
Ajoutons un membre donnée m_OrderSet dans la définition de la classe document CDBDoc
et en insérant le fichier "DBOrderSet.h" dans "DBDoc.h"
# include "DBSet.h"
# include "DBOrderSet.h"
class CDBDoc : public CDocument
{
protected :
CDBDoc ();
DECLARE_DYNCREATE (CDBDoc)
public :
CDBSet m_dBSet;
CDBOrderSet m_OrderSet;
|
|
| auteur : Gabrielly | C'est en appelant le membre
CString CDatabase:: GetDatabaseName () const
|
de CDatabase pour obtenir le nom de la base de données.
Pour obtenir le nom de la table attachée au recordset c'est le membre
const CString& CRecordset:: GetTableName () const
|
void CDBView:: OnInitialUpdate ()
{
m_pSet = & GetDocument ()- > m_dBSet;
m_pSet- > m_strSort = " [ProductID] " ;
CRecordView:: OnInitialUpdate ();
if (m_pSet- > IsOpen ())
{
CString strTitle = m_pSet- > m_pDatabase- > GetDatabaseName ();
CString strTable = m_pSet- > GetTableName ();
if (! strTable.IsEmpty ())
strTitle + = _T (" : " ) + strTable;
GetDocument ()- > SetTitle (strTitle);
}
GetParentFrame ()- > RecalcLayout ();
ResizeParentToFit ();
}
|
En général, vous devez vous assurer que vous obtenez une chaîne retournée par la fonction
GetTableName().
Plusieurs conditions peuvent empêcher la définition d'un nom de table,par exemple le jeu de lignes peut faire intervenir plusieurs tables.
|
| auteur : Gabrielly | Le transfert de données entre la base de données et le jeu de lignes a lieu par un appel direct à CRecordset::Open() de l'objet jeu de lignes.
Pour vérifier que le jeu est déjà ouvert on appelle CRecordset::IsOpen().
Dans l'exemple de notre programme le membre OnInitialUpdate de la classe de base de notre vue
CRecordView appelle la fonction membre Open() de l'objet jeu de lignes.
C'est pourquoi la définition du membre m_strSort pour le tri est définit avant l'appelle à
OnInitialUpdate de la classe de base.
void CDBView:: OnInitialUpdate ()
{
m_pSet- > m_strSort = " [ProductID] " ;
CRecordView:: OnInitialUpdate ();
}
|
Voici le code de CRecordView::OnInitialUpdate() définit dans <DBVIEW.cpp>
void CRecordView:: OnInitialUpdate ()
{
CRecordset* pRecordset = OnGetRecordset ();
ASSERT (pRecordset ! = NULL );
if (! pRecordset- > IsOpen ())
{
CWaitCursor wait;
pRecordset- > Open ();
}
CFormView:: OnInitialUpdate ();
}
|
On peut ouvrir directement le jeu sans passer par OnInitialUpdate par un appel directe à
Open() du jeu.
|
Consultez les autres F.A.Q.
|
|