Zurueck zum Inhaltsverzeichnis
12. Datenbanken
Datenbankmanagementsysteme (DBMS) wie z.B. MS Access oder Oracle bieten eine geordnete Sammlung von Daten, die man sich vereinfacht in Tabellenform vorstellen kann. Die Spalten der Tabelle sind die Felder mit den entsprechenden Feldnamen (z.B. Name, Vorname, Telefon) und die Reihen die aufeinander folgenden Datensätze. Ein wichtiges Abfragewerkzeug in diesem Zusammenhang ist die Sprache SQL.
Wie kann man aber auf solche Datenbanken in der Windows-Programmierung mit C++/MFC zugreifen?
Zunächst
erstellen wir der Einfachheit halber eine ganz einfache "Datenbank" in
MS Excel. Wir
wählen dieses Kalkulationsprogramm, in dem man nebenbei auch Daten
speichern
kann, weil es einfach zu bedienen und auf vielen Rechnern beheimatet
ist.
Ansonsten werden wir zu sehr von den Details der DBMS abgelenkt. Wir
wollen
aber rasch mit der MFC-Programmierung beginnen.
Als Feldnamen verwenden wir Name, Vorname und Hobby.
Geben Sie dem Excel-Tabellen-Bereich
"Tabelle1!$A$1:$C$7" den Namen "ExcelTestDatenbank".
Das funktioniert z.B. über das
Excel-Menü "Einfügen - Namen - Festlegen".
Speichern Sie die Tabelle bitte unter dem
Filenamen
"C:\Daten.xls" ab.
Damit verfügen wir nun über eine
kleine
Testdatenbank.
Im nächsten Schritt bauen wir uns ein "MFC-Lese-Programm" auf diese Datenbank:
Wir erstellen ein neues Projekt
(Menü Datei-Neu-Projekte) namens "ExcelDaten". Als Typ wählen
wir MFC-Anwendungs-Assistent(exe).
In Schritt 1 entscheiden wir für SDI.
Im
Schritt 2 erfolgt nun die entscheidende Auswahl:
Hier wählen wir "Datenbankansicht
mit Dateiunterstützung". Durch diese Auswahl wird der Button
"Datenquelle" aktiviert.
Nach Betätigung dieser
Schaltfläche öffnet sich der Dialog "Datenbankoptionen". Wir
können hier zwischen ODBC, DAO und OLE DB wählen.
Wir entscheiden uns für ODBC (Open
Database Connectivity: Standardprotokoll für
den
Zugriff auf relationale Datenbanken, die auf SQL basieren) und
wählen
dort Excel-Dateien.
Den Recordset-Typ belassen wir
zunächst auf "Snapshot".
Nach "OK" gelangen wir zu "Arbeitsmappe
auswählen".
Hier wählen wir unser File "C:\Daten.xls".
Die nächste Auswahl wird gefordert:
Denn in einer Excel-Arbeitsmappe
könnte eine Vielzahl von Namensbereichen existieren.
Wir wählen unseren Namensbereich
"ExcelTestDatenbank".
Damit ist Schritt 2 abgeschlossen.
Die restlichen Schritte akzeptieren wir
ohne
Änderungen.
In Schritt 6 sehen wir folgende
Zusammenstellung:
Als Basisklasse für unsere View
empfiehlt uns der Assistent die MFC-Klasse CRecordView.
Das ist eine gute Wahl für den Anfang.
Bevor wir unsere Dialogfläche bearbeiten, unternehmen wir eine Wanderung durch das erzeugte Code-Gerüst, um die Verbindung zu unserer Datenbank zu verfolgen. Bedingt durch unsere übersichtliche Test-Datenbank sieht der Header und die Implementierung der Klasse CExcelDatenSet sehr einfach aus:
// ExcelDatenSet.h : Schnittstelle der Klasse CExcelDatenSet
class
CExcelDatenSet : public CRecordset
{
public:
CExcelDatenSet(CDatabase*
pDatabase = NULL);
DECLARE_DYNAMIC(CExcelDatenSet)
// Feld-/Parameterdaten
//{{AFX_FIELD(CExcelDatenSet,
CRecordset)
CString
m_Name;
CString m_Vorname;
CString m_Hobby;
//}}AFX_FIELD
// Überladungen
//
Vom Klassenassistenten generierte Überladungen virtueller
Funktionen
//{{AFX_VIRTUAL(CExcelDatenSet)
public:
virtual
CString GetDefaultConnect(); //
Standard-Verbindungszeichenfolge
virtual
CString GetDefaultSQL(); //
Standard-SQL für Recordset
virtual
void DoFieldExchange(CFieldExchange* pFX); //
RFX-Unterstützung
//}}AFX_VIRTUAL
...
};
// CExcelDatenSet Implementierung
IMPLEMENT_DYNAMIC(CExcelDatenSet, CRecordset)
CExcelDatenSet::CExcelDatenSet(CDatabase*
pdb) : CRecordset(pdb)
{
//{{AFX_FIELD_INIT(CExcelDatenSet)
m_Name =
_T("");
m_Vorname =
_T("");
m_Hobby =
_T("");
m_nFields = 3;
//}}AFX_FIELD_INIT
m_nDefaultType =
snapshot;
}
CString
CExcelDatenSet::GetDefaultConnect()
{
return _T("ODBC;DSN=Excel-Dateien");
}
CString
CExcelDatenSet::GetDefaultSQL()
{
return _T("[ExcelTestDatenbank]");
}
void
CExcelDatenSet::DoFieldExchange(CFieldExchange* pFX)
{
//{{AFX_FIELD_MAP(CExcelDatenSet)
pFX->SetFieldType(CFieldExchange::outputColumn);
RFX_Text(pFX,
_T("[Name]"),
m_Name);
RFX_Text(pFX,
_T("[Vorname]"),
m_Vorname);
RFX_Text(pFX,
_T("[Hobby]"),
m_Hobby);
//}}AFX_FIELD_MAP
}
Die Code-Zeilen, die unsere spezielle
Datenbank berühren, wurden oben hervorgehoben.
Zum einfachen Erkennen des Zusammenhangs
sind
diese Zeilen hier noch einmal dargestellt:
//C___Set
CString m_Name;
CString m_Vorname;
CString m_Hobby;
m_Name = _T("");
m_Vorname = _T("");
m_Hobby = _T("");
CString
CExcelDatenSet::GetDefaultConnect()
{
return _T("ODBC;DSN=Excel-Dateien");
}
CString
CExcelDatenSet::GetDefaultSQL()
{
return _T("[ExcelTestDatenbank]");
}
RFX_Text(pFX,
_T("[Name]"), m_Name);
RFX_Text(pFX,
_T("[Vorname]"), m_Vorname);
RFX_Text(pFX,
_T("[Hobby]"), m_Hobby);
Zusammengefaßt kann man sagen,
daß wir hier einen ODBC-Zugriff auf eine Excel-Datei
durchführen. Der ODBC-Begriff DSN steht
übrigens für Data Source Name. Der
Name unserer Datenbank ist ExcelTestDatenbank.
Die Datenbank-Felder Name, Vorname und Hobby werden in die
CString-Variablen
m_Name, m_Vorname und m_Hobby der Klasse CExcelDatenSet
überführt.
Was hier völlig fehlt, ist unser
Filename
"C:\Daten.xls". Verfolgen Sie die Auswirkungen hiervon bitte genau.
Der obige Code ist der zentrale Teil der
Datenbankanbindung.
Alles, was nun folgt, unterstützt
nur
die Auswahl und Visualisierung dieser Daten.
Zur Darstellung der Daten kehren wir in
unsere
Ressource IDD_EXCELDATEN_FORM zurück.
Wir benötigen drei Edit-Felder zur
Datenanzeige,
denen wir drei Static-Felder zur Anzeige des Feldnamens vorstellen.
Damit wir einfach auf den Inhalt dieser
Edit-Felder
zugreifen können, erzeugen wir mit dem MFC-Klassen-Assistenten die
Member-Variablen
m_Name, m_Vorname und m_Hobby.
Diese Variablen gehören zur Klasse CExcelDatenView (nicht verwechseln mit den gleichnamigen Variablen der der Klasse CExcelDatenSet).
Nun können wir die einzelnen Felder eines Datensatzes unseren Edit-Feldern zuordnen. Es fehlt uns noch ein Zeiger auf die einzelnen Datensätze. Aber MFC hat schon wie gewohnt vorgesorgt:
// ExcelDatenView.h : Schnittstelle der Klasse CExcelDatenView
class CExcelDatenSet;
class
CExcelDatenView : public CRecordView
{
protected: // Nur aus Serialisierung erzeugen
CExcelDatenView();
DECLARE_DYNCREATE(CExcelDatenView)
public:
//{{AFX_DATA(CExcelDatenView)
enum { IDD =
IDD_EXCELDATEN_FORM };
CExcelDatenSet*
m_pSet;
CString m_Name;
CString
m_Vorname;
CString m_Hobby;
//}}AFX_DATA
Der Klassen-Assistent hat der View-Klasse einen Zeiger auf die Set-Klasse zugefügt. Damit können wir auf einfache Weise in unserer Datenbank navigieren.
Zunächst wollen wir, daß das
Programm
beim Start zu unserem ersten Datensatz springt.
Hierzu verwenden wir die Member-Funktion
OnInitialUpdate().
Dort fügen wir folgenden Code hinzu:
void
CExcelDatenView::OnInitialUpdate()
{
m_pSet =
&GetDocument()->m_excelDatenSet;
CRecordView::OnInitialUpdate();
GetParentFrame()->RecalcLayout();
ResizeParentToFit();
OnRecordFirst();
}
Es gibt aber noch keine Funktion OnRecordFirst? Stimmt, das werden wir aber sofort ändern:
Im Klassen-Assistent gibt es unter
Nachrichtenzuordnungstabellen die Bezeichner ID_RECORD_FIRST, ..._LAST,
..._NEXT und ..._PREV.
Diesen vier Nachrichten ordnen Sie bitte
vier
Funktionen zu. Bei ID_RECORD_FIRST ist dies OnRecordFirst().
Zusätzlich lagern wir zur Vermeidung von Wiederholungen die Darstellung der Daten in die eigene Member-Funktion "void ShowData()" aus:
Das sieht nun wie folgt aus:
void
CExcelDatenView::OnRecordFirst()
{
m_pSet->MoveFirst();
ShowData();
}
void
CExcelDatenView::OnRecordLast()
{
m_pSet->MoveLast();
ShowData();
}
void
CExcelDatenView::OnRecordNext()
{
m_pSet->MoveNext();
ShowData();
}
void
CExcelDatenView::OnRecordPrev()
{
m_pSet->MovePrev();
ShowData();
}
void CExcelDatenView::ShowData()
{
m_Name = m_pSet->m_Name;
m_Vorname = m_pSet->m_Vorname;
m_Hobby = m_pSet->m_Hobby;
UpdateData(FALSE);
}
Nun kompilieren und starten wir. Aha,
da möchte
das Programm noch wissen, welche Arbeitsmappe wir denn gerne
hätten.
Das ist der fehlende Filename in unserem
Programm:
Wählen Sie bitte C:\Daten.xls aus.
... und schon greifen wir auf den
ersten Datensatz
unserer Datenbank zu.
Sie sehen oben in der Toolbar die vier
Pfeilsymbole.
Diese entsprechen den Bezeichnern ID_RECORD_FIRST, ..._LAST, ..._NEXT
und
..._PREV, die zu unseren Funktionen OnRecord...() führen. Damit
navigieren
wir durch die Datenbank.
An diesem einfachen Beispiel haben Sie gesehen, welche hervorragenden Werkzeuge MFC bietet, um mittels C++ mit Datenbanken ins Gespräch zu kommen. Jetzt ist der richtige Zeitpunkt, um das Problem mit dem fehlenden Filenamen anzugehen. Das Thema heißt ODBC-Treiber. Schauen Sie sich noch einmal die entscheidende Stelle im Code an:
CString
CExcelDatenSet::GetDefaultConnect()
{
return _T("ODBC;DSN=Excel-Dateien");
}
Als DSN wurde hier allgemein
Excel-Dateien übergeben. Wo finden wir diese DSN?
Gehen Sie zu Ihrem Desktop und öffnen
Sie
Arbeitsplatz - Systemsteuerung - ODBC-Datenquellen (32 bit):
Dort fügen wir einen neuen Namen
ein, der mit unserer Arbeitsmappe in C:\Daten.xls verbunden ist.
Durchlaufen Sie bitte die Dialoge nach
Betätigen
der Schaltfläche "Hinzufügen" und wählen Sie als
Datenquellenname
"ExcelTest" und als Arbeitsmappe "C:\Daten.xls". Nun verfügen Sie
über
einen selbst erstellten ODBC-Treiber auf diese Arbeitsmappe.
Im nächsten Schritt wird nun DSN=ExcelTest
in unser Programm eingefügt:
CString
CExcelDatenSet::GetDefaultConnect()
{
return _T("ODBC;DSN=ExcelTest");
}
Ab jetzt startet das Programm direkt zu
unserer
Arbeitsmappe durch, ohne sich danach zu erkundigen.
wird fortgesetzt.
Zurueck zum Inhaltsverzeichnis