Die Frage der Datenbankabstraktion

.. Gedanken, villeicht mehr auch eine kleine Einführung, die ich mir zum design meines codes bezüglich der Interaktion mit der Datenbank gemacht habe. Ich erhebe kein Recht auf Vollständigkeit.

Vor langer Zeit habe ich mir eine Klasse heruntergeladen, noch zu Zeiten der Programmierung mit PHP4, und mit ihr all meine Interaktion mit der Datenbank vorgenommen. Gemeint ist natürlich die Datenbank Mysql. Bewusst war mir damals nicht, was ich da eigentlich benutzt habe. Aber es war schon recht komfortabel und einfach.

Da nun in jüngster Zeit ein neues Projekt entwickelt wird, kam die Frage nach der “Lösung” des Datenbankproblems auf. Schnell soll die Interaktion mit der Datenbank sein, aber auch erweiterbar und vor allen Dingen soll ohne großartige Fummelei am code die Datenbank an sich geändert werden können.

Zwangsläufig kommt man auf das Stichwort Abstraktion. Das sagt Wikipedia dazu :

Das Wort Abstraktion bezeichnet meist den induktiven Denkprozess des Weglassens von Einzelheiten und des Überführens auf etwas Allgemeineres oder Einfacheres.

Also beschäftige dich nicht mit dem direkten Ansprechen der Datenbank, sondern schaffe eine Möglichkeit es zu vereinfachen (in diesem Fall auch verallgemeinern). Folglich war das Ziel klar. Will ich den code elegant erweiterbar halten muss eine Abstraktion her.
Wenn Erweiterungen von mir oder anderen Personen nachträglich eingefügt werden. So muss beim Ändern der Struktur (z.b. der Umstieg von Mysql auf eine andere Datenbank) keine Änderung am code vorgenommen werden. Einzig und allein die Klasse, die für die Datenbankabstraktion verantwortlich ist, muss ausgetauscht werden. Ein einfaches Beispiel:

//datenbank klasse
    class Datenbank {
        public function fetchAll($query) {
        //Mysql spezifische Abfrage!
    }
}

//Die Funktion liefert ein array mit den Ergebnismengen.
array Datenbank::fetchAll(string $Abfrage)

Uninterressant ist dabei was mit dem Ergebnis passiert. Der Code hinter der Funktion managed eben das “holen” (fetchAll) der Daten aus der Mysql Datenbank mit Mysql spezifischem code. Möchte nun ein Kunde Sqlite benutzen, so änder ich ausschließlich den code der Datenbank Klasse. Da dieser in einer Datei liegt, lässt sich die Änderung leicht und umkompliziert durchführen.

class Datenbank {
    public function fetchAll($query) {
        //Sqlite spezifische Abfrage!
    }
}

Bleibt noch zu sagen, dass Entwickler, die mit der Abstraktion arbeiten, mit der Datenbank einfacher umgehen können. Statt denselben code für das Holen von Zeilen aus der Datenbank mehrfach zu schreiben, kann er in eine Funktion der Datenbankklasse gepackt (gekapselt) werden. Wieder ist code gespart und einfach wartbar gemacht worden.

Vorteile einer Abstraktionsschicht (abstraction layer) :

  1. Geringere Fehlerquellen beim Ändern der Datenbankstruktur (Die Änderungen werden nur in der Abstraktionsklasse vorgenommen). Code, von wem auch immer, bleibt unangetastet.
  2. Flexible Programmstruktur. Das Ändern des codes hinsichtlich der Datenbank erfolgt mit dem Wechsel einer Datei.
  3. Die Benutzung der Datenbank wird vereinfacht.

Nachteile:

  1. Entwickler müssen lernen, wie sie die Abstraktionsklasse nutzen.
  2. Datenbanken unterstützen nicht dieselben Funktionen, d.h. wird mit der Abstraktionsklasse eine spezifische Funktion unterstützt, so muss die Funktion bei einer anderen Datenbank unter Umständen simuliert werden.

Da es hier auch um PHP5 geht werde ich das Rad weiterspinnen. Grundsätzliche sollte immer die aktuellste stabilste PHP Version benutzt werden (sofern es die Software erlaubt). In diesem Fall 5.2.6.
Interessant ist die Neuigkeit, die seit 5.1 fest im PHP Paket integriert ist: PHP Data Objects (kurz PDO). PDO gibt es auch für PHP 5.0, aber nur als PECL Erweiterung. PDO ist eine solche Abstraktionsschicht. Mit ihr beschäftigt man sich beim programmieren nicht damit, wie muss Mysql, Sqlite, etc. angesprochen werden, sondern wie spreche ich mit der Datenbank, die grade zur Verfügung steht. Im Hintergrund werden Treiber, die PHP zur Verfügung stehen genutzt um die Verbindungen zu den Datenbanken herzustellen.
Beispielcode:

//Löschen von Datensätze mit irgendeiner Datenbank
echo db->exec('DELETE FROM test_table LIMIT 3');

//Die Ausgabe wäre, sofern mehr als oder 3 Datensätze vorhanden waren
3

PDO::exec() übernimmt nun die Aufgabe die aktuelle Datenbank anzusprechen die Abfrage auszuführen und die Anzahl der betroffenen Zeilen zurückzugeben. Wir benutzen also eine PHP-interne Datenbankabstraktionsschicht!
Sie ist auch keine in PHP selbst geschriebene Erweiterung (wie z.b. das PEAR-Paket DB - auch eine Abstraktionsklasse) sondern ist in C geschrieben und dementsprechend wesentlich schneller als alles andere.

Nun kann man sich also endgültig vom Schreiben einer Abstraktionsschicht entfernen und PDO nutzen. Leider gibt es dabei auch einen Haken. PDO ab PHP5.1 ist zwar immer im Paket mit dabei, aber muss der webhoster auch PHP5.1 mit z.b. Mysql Unterstützung zur Verfügung stellen. Das bedeutet es ist unter Umständen nicht überall verwendbar man muss Alternativen mit entwickeln.
Aber wie erreiche ich es nun, dass mein code so flexibel bleibt (Stichwort: Datenbankwechsel). Man muss ja schließlich noch jedesmal eine Verbindung zur Datenbank herstellen. Da kommt die Abstraktion der Datenbankverbindung ins Spiel. Es wird sich also in Zukunft nur noch darauf beschränken die Verbindung der PDO-Klasse zur Datenbank zu abstrahieren. Ein kleines Beispiel:

class MysqlPDO extends PDO {
    public function __construct() {
        //Spezifischer Code zur Mysql-Datenbank-Verbindung
        //Ein vollständiges Beispiel findet sich weiter unten
    }
}

$db = new MysqlPDO();

So wird wieder garantiert nur einen Teil des codes ändern zu müssen, falls beispielsweise eine andere Datenbank genutzt werden soll.

Das war also mein aller erster Blogeintrag in meinem Leben, ich hoffe man kann meinen Gedanken einiegermaßen folgen und ich habe zwischendurch nichts vergessen. Jetzt folgt also noch die komplette Klasse einer Verbindungsabstraktion mit Mysql:

//Der Inhalt der Datei db.access.php

$dbHost = 'localhost';
$dbUser = 'Benutzer';
$dbPassword = 'Passwort';
$dbDatabase = 'Datenbank';

$dbCharset = 'utf-8';

//Der code an sich

class MysqlPDO extends PDO {
    public function __construct() {
        //Einfügen der Zugangsdaten
        $dbHost =
        $dbUser =
        $dbPassword =
        $dbDatabase =
        $dbCharset = '';
        require_once 'db.access.php';

        //Ein dsn beschreibt die Zugangsdaten und die Art der Datenbank
        //hier: Mysql
        $dsn = 'mysql:dbname='.$dbDatabase.';host='.$dbHost;

        //Verbindung herstellen
        //Ganz wichtig ist hier das Auffangen eines Fehlers beim Herstellen der Verbindungsonst werden die Zugangsdaten angezeigt!
        try {
            parent::__construct($dsn, $dbUser, $dbPassword);
        } catch(PDOException $e) {
            echo 'Connection failed: '. $e->getMessage();
        }

        //Das charset noch angeben
        $this->query('SET NAMES "'.$dbCharset.'"');
    }
}

$db = new MysqlPDO();

//Beispiel von oben
echo $db->exec('DELETE FROM test_table LIMIT 3');

//Ausgabe
3

Link zur PHP Dokumentation über PDO (leider noch auf Englisch) :

# http://de2.php.net/manual/de/book.pdo.php

Schlagworte: , , ,

Eine Antwort hinterlassen

Sie müssen angemeldet sein, um kommentieren zu können.