Montag, 9. Juni 2014

SQLite in Embarcadero RAD Studio XE 4 einbinden (C++)



Das Originale (und besser hervorgehobene) Tutorial findet ihr im Forum unter http://www.filmdatenbank-manager.de/forum/show/index.php?page=Thread&threadID=159. Dort ist das ganze etwas besser lesbar, da ich es hier nur grob in den Blog übertragen habe. Trotzdem viel Spaß damit, wem es etwas nützt.

================================================================


Wenn ihr eine schmale Datenbank in eure C++ Applikation einbinden wollt UND ihr dabei das Embarcadero RAD Studio XE 4 (geht auch bei anderen Versionen, ich habe es in diesem Fall unter der XE 4 Version gemacht) verwendet, dann ist dies was nun folgt eine Variante um das eigene Projekt umsetzen zu können.
Ein Tutorial für die SQLite/C++ Verwendung AUßERHALB des Embarcadero Studios (also ohne dessen fertige Komponenten) kann ich gerne nachliefern, FALLS dies gewünscht wird.

1.) Was ist SQLite ?



SQLite ist eine vollständig in C geschriebene Programmbibliothek, welche ein relationales Datenbanksystem enthält. Es ist prädestiniert für den Einsatz in eingebetteten Datenbanksystemen und unterstützt einen Großteil des SQL-92 Sprachstandards. Für diverse Programmiersprachen (z.B. Java) stehen sogenannte Wrapper sowie ODBC-, dbExpress und JDBC-Treiber zur Verfügung. SQLite erzwingt keine Typsicherheit: Fehlerhafte Eingaben werden in der Regel akzeptiert und in Zeichenketten umgewandelt.

Soviel dazu...

2.) Was wird benötigt ?



  •     Embarcadero RAD Studio XE ... :P



*Anmerkung : Ich verwende das FF Plugin "SQLite Manager" mit dem SQLite3 Shell-Frontend in Kombination, da ich diesen bereits bei einem anderen Projekt, bei welchem ich eh´ viel der Informationen aus dem Browser geholt habe, verwendet habe. Das mache ich nur um das Tutorial einsteigerfreundlicher zu halten, denn so können wir komfortabel die Datenbank im FF-Plugin erstellen ohne uns um den SQL Code zur Erstellung der Datenbank einen Kopf machen zu müssen.
Fortgeschrittene und Experten können das natürlich sofort in sqlite3 erledigen.


3.) Unsere Beispieldatenbank



OK, hier ist unser kleines Praxisbeispiel als Entity-Relationship-Modell in der Barker Darstellung.


Und hier eine Phase weiter als Relationenmodell.

    Computer(Computer_id, CPU, RAM, Vendor)
    Desktop(Computer_id, Monitortyp)
    Notebook(Computer_id, integrLaufwerk)



4.) Umsetzung als SQLite DB


4.1.) Installation von SQLite3 (falls noch nicht geschehen)



Falls ihr sqlite noch nicht installiert habt, dann folgt obigen Link ("Kapitel" 2) und downloadet die 2 Archive sqlite-shell-win32-x86-3080500.zip und
sqlite-dll-win32-x86-3080500.zip. Entpackt diese und kopiert alle Dateien der 2 entpackten Ordner in EINEN gemeinsamen Ordner, sodass ihr
im Ordner die 3 Dateien sqlite3.def, sqlite3.dll und sqlite3.exe liegen habt.
Nennt den Ordner z.B. "SQLite" und kopiert ihn auf eure Hauptpartition (z.B. C:\SQLite\...").
Nun müsst ihr diesen Pfad noch in die Umgebungsvariablen des Systems schreiben, um das Tool von einem beliebigen Ort aus der Konsole heraus öffnen zu können.
Für alle die nicht mehr ganz wissen wie das geht:

  •     Rechtsklick auf "Arbeitsplatz" bzw. "Computer" bei den neueren Windows OSs.
    auf Eigenschaften klicken
  •     im sich öffnenden Fenster links auf "Erweiterte Systemeinstellungen" klicken
  •     im Reiter/Tab "Erweitert" auf "Umgebungsvariablen" klicken
    in der Liste der "Systemvariablen" die Variable Namens "Path" anklicken und bearbeiten
  •     hängt an den letzten Eintrag in der Liste ein ";" (ohne Anführungsstriche), um einen neuen Eintrag hinzuzufügen
  •     wenn ihr das getan habt, schreibt nun euren Pfad hinein (z.B. "C:\SQLite\") --> wieder ohne Anführungsstriche
    Fertig !



4.2.) Erstellen der Datenbank



Kommen wir nun zur Erstellung der Datenbank. Am Ende des Tutorials könnt ihr die Beispieldaten aber auch einfach herunterladen, falls ihr nicht jeden einzelnen Schritt durchgehen wollt.

Startet Firefox und öffnet unter "Extras" den "SQLiteManager". Das Tool ist ziemlich selbsterklärend, klickt auf die Schaltfläche "Neue Datenbank" und gebt ihr den Namen "MiniTutDB" und erstellt die neuen Tabellen der DB wie auf den Screenshots zu sehen ist über die Schaltfläche "Tabelle erstellen".




Seid ihr damit fertig, dann speichert den Befehl zur Erstellung unserer Beispieldatenbank über einem Klick auf das Menü "Datenbank\Datenbank exportieren".
Speichert die Datei unter dem Namen "MiniTutDB.sql" ab. Diese Datei enthält nun den nötigen SQL Code, den wir in sqlite3 benötigen, um unsere Datenbank in einer "DB"-Datei abzuspeichern, welche wir dann im C++ Programm verwenden werden.

Öffnet die Konsole, indem ihr [WindowsTaste]+R drückt und "cmd" gefolgt von ENTER eingebt. Oder ihr sucht in eurer Programmliste nach "cmd" :).
In der Konsole angekommen gilt es nun folgende Schritte zu erledigen:

    gebt nun "sqlite3", gefolgt von ENTER ein (es sollten nun Versionsinformationen etc. von sqlite3 erscheinen)
    Das Programm wartet nun auf eure Instruktionen (zu erkennen am blinkendem Cursor und am "sqlite> ")
    Die oben erwähnten Fortgeschrittenen und Experten (die das hier nicht bräuchten) könnten die Datenbank jetzt auch rein in der Konsole erzeugen, alle anderen die es oben über das FF-Plugin gelöst haben, müssen die nötigen Befehle jetzt nicht eingeben sondern verweisen einfach auf unsere Datei "MiniTutDB.sql", welche alle benötigten Befehle beinhaltet
    das machen wir folgendermaßen : gebt nun .read "Pfad zur Datei" ein
    WICHTIG: Der Pfad ist falls er Leerzeichen enthält in Anführungszeichen zu setzen UND die Verzeichnistrenner sind nicht wie in der Windows-Schreibweise durch einen Backslash (\), sondern wie bei Unix durch einen Slash (/) anzugeben. Bsp : .read "C:/Users/username/Desktop/SQLite.Minitut/MiniTutDB.sql"
    es sollte keinen Fehler geben und sqlite3 wartet auf eure nächste Instruktion
    gebt nun .save "Pfad zur Ausgabedatei" ein. Diese endet mit der Endung ".db", also in meinem Falle
    .save "C:/Users/username/Desktop/SQLite.Minitut/MiniTutDB.db"
    Gratulation, die Datenbank sollte nun im angegebenen Verzeichnis erstellt worden sein, beendet "sqlite3" nun mit [Strg.]+C und dann das Terminal mit "exit"



5.) Unsere Beispielapplikation (Embarcadero RAD Studio XE C++)



OK, kommen wir ans Eingemachte. Aber nicht zuviel erwarten :D. Da das nur ein Minitutorial ist, ist das Beispielprog alles andere als ein vollwertiger Aufsatz für unsere darunterliegende DB. Ich zeige nur die Verwendung zweier Selektionsabfragen auf 2 verschiedene Arten.
Die erste Variante liefert die Ergebnisse des ResultSets einfach in ein Memo, die zweite Variante ist mit einer Tabellenartigen Ausgabe verknüpft.
Dem Programm fehlen also jegliche Möglichkeiten schreibend auf die DB zugreifen zu können um auch etwas nützliches mit dieser anzustellen.
Dies ist aber kein Thema, da dass Programm schnell erweitert werden kann, das ist aber SQL und hat mit diesem Rezept eher weniger zu tun.

Ladet euch die Projektdateien einfach am Ende des Tutorials herunter und startet es in "Embarcadero RAD Studio XE", da ich jetzt hier nicht auf jeden einzelnen Erzeugungsschritt eingehe und stattdessen nur auf die wichtigsten Einstellungen im Objektinspektor und den Code eingehen werde.

Eine Anmerkung noch : TSQLQuery ist ein unidirektionaler Datensatz. Deshalb ist es uns nicht wie bei anderen Datensätzen möglich, mehrere Einträge im Speicher zu puffern und können demzufolge nur mit den "First"- und "Next"-Methoden durch den Datensatz navigieren. Mit diversen anderen Query-Komponenten geht das, und man kann eine einfache DB-Grid an seine Query binden.

Das Programmlayout:






Die "Captions" "via DBGrid" sind NICHT mehr korrekt, da ich die Darstellung aus dem oben genannten Grund auf LiveBindings basieren lasse !!!

Die Programmkonfiguration:


Bevor es an den Code geht, kommen wir zur Konfiguration bzw. der Einstellung der Komponenten, denn diese müssen zur Funktionstüchtigkeit erst untereinander verknüpft werden.


  •     SQLConnection1 : stellt im Objektinspektor die Eigenschaften ConnectionName auf  SQLITECONNECTION und Driver auf Sqlite
  •     DataSource1 : stellt im Objektinspektor die Eigenschaft DataSet auf SQLQuery1
  •     SQLQuery1 : stellt im Objektinspektor die Eigenschaft SQLConnection auf SQLConnection1
  •     DataSetProvider1 : stellt im Objektinspektor die Eigenschaft DataSet auf SQLQuery1



Um unsere Tabellenausgabe zu bekommen, klicken wir einfach in die untere Groupbox (Groupbox2) mit einem rechtsklick ins Formular und wählen "Visuell binden" aus.
Wählt den "LiveBindings-Experten" (siehe Bild)







Nun wählt folgendes:





  • wählt "Gitter mit einer Datenquelle verknüpfen" und auf "weiter" klicken
  • wählt im Reiter/Tab "Neues Gitter" die Option "TStringGrid" und auf "weiter" klicken
  • wählt im Reiter/Tab "vorhandene Datenquelle" die Option "DataSource1" und auf "Fertigstellen" klicken

Code (nur main.cpp):   

C++ Quelltext

//---------------------------------------------------------------------------
 
#include <vcl.h>
#pragma hdrstop
 
#include "main.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
 
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
    : TForm(Owner)
{
}
//---------------------------------------------------------------------------
 
// Verbindung zur Datenbank aufbauen
void __fastcall TForm1::Button1Click(TObject *Sender)
{
    // Pfad zur DB angeben, damit der Treiber darauf arbeiten kann
    Memo1->Lines->Add(ReplaceStr(ExtractFilePath(Application->ExeName) +
                                "MiniTutDB.db", ".", ""));
    SQLConnection1->Params->Values["Database"] = ReplaceStr(ExtractFilePath(Application->ExeName) +
                                "MiniTutDB.db", ".", "");
    try
    {
        // Verbindung aufnehmen
        SQLConnection1->Connected = true;
        Button2->Enabled = true;
        Button3->Enabled = true;
        Memo1->Lines->Add("Verbindung aufgebaut");
    }
    catch(EDatabaseError& E)
    {
        MessageDlg(AnsiString("Fehler !\n") + AnsiString(E.Message) , mtError,
                   TMsgDlgButtons()<<mbOK, 0);
        Button2->Enabled = false;
        Button3->Enabled = false;
    }
}
//---------------------------------------------------------------------------
 
// 1. Variante --> Allgemeine Auswahlabfrage
void __fastcall TForm1::Button2Click(TObject *Sender)
{
    // Deklarationen
    String query;
    TDataSet *rs;
    TStringList *list;
    int i = 0;
 
    // Allgemeine Abfrage (den ersten Eintrag holen)
    query = "SELECT * FROM Computer WHERE Computer_id=1;";
 
    try
    {
        // Abfrage ausführen
        SQLConnection1->Execute(query, NULL, rs);
        // Ergebnisse im Memo ausgeben
        if( !(rs->IsEmpty()) )
        {
            rs->First();
            list = new TStringList();
            rs->GetFieldNames(list);
            while( !(rs->Eof) )
            {
                for( ; i < list->Count; i++)
                    Memo1->Lines->Add(rs->FieldByName(list->Strings[i])->AsString);
                rs->Next();
            }
        }
    }
    catch(Exception &exception)
    {
      Memo1->Lines->Add("FEHLER : " + exception.Message);
      Button2->Enabled = false;
      Button3->Enabled = false;
    }
}
//---------------------------------------------------------------------------
 
// 2. Variante --> Allgemeine Auswahlabfrage
/* TSQLQuery ist ein unidirektionaler Datensatz. Deshalb ist es uns nicht
   wie bei anderen Datensätzen möglich, mehrere Einträge im Speicher zu puffern
   und können demzufolge nur mit den "First"- und "Next"-Methoden durch den
   Datensatz navigieren.
*/
void __fastcall TForm1::Button3Click(TObject *Sender)
{
    // Sollte zwar nie der Fall sein, aber man weiß ja nie...
    if (RadioGroup1->ItemIndex < 0 && RadioGroup1->ItemIndex > 2)
    {
        MessageDlg(AnsiString("Ungültige Option. Breche ab..."), mtError,
                   TMsgDlgButtons()<<mbOK, 0);
        exit(-1);
    }
 
    SQLQuery1->Close();
    SQLQuery1->SQL->Clear();
 
    switch(RadioGroup1->ItemIndex)
    {
        case 0 : {SQLQuery1->SQL->Add("Select * from Computer;"); break;}
        case 1 : {SQLQuery1->SQL->Add("Select * from Desktop;"); break;}
        case 2 : {SQLQuery1->SQL->Add("Select * from Notebook;"); break;}
    }
 
    // Es ist nur eine SELEKTION, deshalb "Open", für die anderen Aggregation
    // dann ".ExecSQL" nehmen
    //SQLQuery1->ExecSQL(true);
    SQLQuery1->Open();
}
//---------------------------------------------------------------------------



Beispielausgabe:




|-- Zweite Variante (oben) Erste Variante (unten) --|




Wie ihr seht, habe ich (ohne es explizit zu erwähnen) noch 2 Einträge in unsere oben erstellte Datenbank geschrieben, ansonsten gäbe es ja auch nichts abzufragen :P. Die Beispieldatenbank unten im Download ist bereits mit den 2 Einträgen versehen.

..:: DOWNLOAD UNSERES KLEINEN PROJEKTES ::..

Share-Online : Link


Falls ihr Fehler gefunden habt oder Verbesserungsvorschläge parat habt, dann lasst es mich in diesem Thread wissen !

Reaktionen:

0 Kommentare:

Kommentar veröffentlichen