Aus Linux-Magazin 04/2007

Design Patterns für Datenbankzugriff

© Melodi T, sxc.hu

Egal ob MySQL, PostgreSQL oder Oracle: Die gezielte Anwendung von Design Patterns vereinheitlicht die Datenbankprogrammierung. Das Beispielprojekt implementiert das Facade-Pattern in C# und Mono.

Seit die als “Gang of Four” bekannte Gruppe von Software-Entwicklern das Standardwerk “Design Patterns – Elements of Reusable Object-Oriented Software” veröffentlichte, sind mehr als zehn Jahre vergangen [1]. Heute sind die von Erich Gamma, Richard Helm, Ralph Johnson und John Vlissides beschriebenen Entwurfsmuster in der Entwicklungspraxis nicht mehr wegzudenken.

Die Erkenntnis, dass beim Code-Entwurf bestimmte Zusammenhänge immer wieder auftauchen, gehört nun zum Standardwissen. Denn Software will nicht nur für einen bestimmten Zweck entwickelt werden, sondern soll auch eine ganze Reihe von Nebenbedingungen erfüllen. Eine der fast immer anzutreffenden Bedingungen ist der berechtigte Wunsch der Software-Architekten, dass Code wiederverwendbar, also möglichst leicht verständlich sein soll. Funktionalität allein ist schon seit längerem nicht das einzige Nonplusultra einer guten Software. Sie muss leicht anpassbar, erweiterbar und robust sein.

Datenbank-Muster

Die Gang of Four hat in ihrem Buch ganze 23 Design Patterns ausfindig gemacht und sie in verschiedenen Kategorien zusammengefasst. Darunter fallen unter anderem Patterns, die die Mensch-Maschine-Interaktion strukturieren oder Komplexität verbergen, oder Patterns, die gewährleisten, dass eine Sitzung bestimmte Code-Abschnitte nicht mehrmals ausführt. Als realistischer Anwendungsfall dient im Folgenden die Kommunikation eines Users mit einer für ihn verborgenen Datenbankinstanz. Bei der Implementierung kommen Mono 1.2 und C# zum Einsatz.

Als die Softwarewelt noch übersichtlich war und es einige wenige Hersteller von Datenbank-Standardsystemen gab, fiel die Wahl der geeigneten Plattform wesentlich leichter, als es heute der Fall ist. In einer schnelllebigen Welt wie der Softwarebranche machen neue Datenbanksysteme alten Hasen das Leben immer schwerer. Von MySQL bis zu den rein objektorientierten Systemen wie Db4o ist die Auswahl heute riesig.

Fassade

Da nur wenige Firmen den Wunsch hegen, ihre alten Datenbestände komplett auf neue Plattformen zu hieven, stellt sich ihnen oft die Frage: Wie behalten wir alte Daten, ohne auf neue Technologien verzichten zu müssen? Mit einem passenden Design Pattern lässt sich eine solche Aufgabe meistern. Am besten gestalten Programmierer den Zugriff auf alte und neue Datenbestände von Anfang an transparent, da es für die Verarbeitung der gewonnen Daten unerheblich ist, woher sie stammen und wie das darunter liegende Datenbanksystem im Detail aufgebaut ist.

Das dazu am besten passende Pattern ist Facade [2]. Wie eine Hausfassade bietet es von außen gesehen eine einheitliche Oberfläche, hinter der sich ganz unterschiedlich eingerichtete Zimmer verbergen. Das folgende Beispiel implementiert einen transparenten Zugriff auf eine MySQL- und eine Oracle-Datenbank, bei dem sich der Benutzer nicht darum kümmern muss, manuell eine Datenbankverbindung aufzubauen. Vielmehr gibt es eine gemeinsame Schnittstelle, die SQL-Befehle weiterleitet und Daten zurückliefert.

Abbildung 1 zeigt den Aufbau der implementierten Architektur. Sie verbirgt in Form einer übergeordneten Klasse zwei weitere Klassen, die auf bestimmte Datenbanksysteme spezialisiert sind. Die Oracle-Express-Datenbank (Oracle XE) können Debian-Benutzer über das Non-Free-Repository beziehen. Anwender anderer Distributionen laden es am besten von der Oracle-Homepage herunter [3]. Als Datenbank-Treiber für MySQL dient entweder der nicht weiterentwickelte Bytefx-Treiber, der Teil des Mono-Framework ist, oder der MySQL-Connector des Herstellers.

Abbildung 1: Das Facade-Pattern verbirgt die beiden darunter liegenden Datenbanksysteme. Für den Client ist der Zugriff abstrakt.

Abbildung 1: Das Facade-Pattern verbirgt die beiden darunter liegenden Datenbanksysteme. Für den Client ist der Zugriff abstrakt.

Datenbankspezifisch

Listing 1 zeigt die Klasse für den Zugriff auf eine Oracle-Instanz. Sie enthält neben dem obligatorischen Oracle-Connection-String eine vordefinierte SQL-Anweisung und eine öffentliche Methode. Nachdem die Klasse in Zeile 12 eine Datenbankverbindung geöffnet hat, führt sie in Zeile 18 das SQL-Statement aus Zeile 7 aus. In der While-Schleife ab Zeile 20 setzt sie sukzessive das Ergebnis zusammen, das die Methode in Zeile 28 zurückliefert. Der verwendete »OracleDataReader« ist ein Forward-only-Reader, der nur Leseoperationen unterstützt und deshalb schneller arbeitet als ein bidirektionales Modul.

Listing 1:
Oracle-Klasse

01 public class OraZugriff {
02      private OraConnStr = "Data Source=OraDb;" +
03                            "User ID=User;" +
04                            "Password=Pass;";
05 
06      private OracleConnection dbcon = null;
07      private string OraCmdStr = "SELECT Wert FROM OraTabelle";
08      private StringBuilder OraResult;
09 
10      public string[] OraConnect()
11      {
12          OraConn = new OracleConnection (OraConnStr);
13 
14          OraConn.Open();
15          OracleCommand OraCmd = OraConn.CreateCommand();
16 
17          OraCmd.CommandText = OraCmdStr;
18          OracleDataReader OraReader = OraCmd.ExecuteReader();
19 
20          while (OraReader.Read ()) {
21                OraResult.Append( (string) OraReader["Wert"]);
22 
23          }
24 
25          OraConn.Close();
26          OraReader.Close();
27          OraCmd.Dispose();
28          return OraResult.ToString();
29       }
30 }

Eine weitere Klasse übernimmt den MySQL-Zugriff (Listing 2). Dem Anwender der Klasse bietet sie ähnliche Methoden, verbirgt jedoch die zugrunde liegende Mechanik. Schon der Connection-String offenbart den Unterschied zum Oracle-Zugriff. Natürlich unterscheidet sich auch der Rest des API.

Listing 2:
MySQL-Klasse

01 public class MySqlZugriff
02 {
03 
04     private string MyConnStr = "Server=Srv;" +
05                          "Database=myDb;" +
06                          "User ID=myUser;" +
07                          "Password=myPass;" +
08                          "Pooling=false";
09 
10     private IDbConnection MyConn;
11     private string MyCmdStr = "SELECT Wert FROM MyTabelle";
12     string MyResult;
13 
14     public void MyConnect()
15     {
16        MyConn = new MySqlConnection(MyConnStr);
17        MyConn.Open();
18 
19        IDbCommand MyCmd = MyConn.CreateCommand();
20 
21        MyCmd.CommandText = MyCmdStr;
22        IDataReader MyReader = MyCmd.ExecuteReader();
23 
24        while(MyReader.Read()) {
25 
26            MyResult = (string) MyReader["Wert"];
27            Console.WriteLine("Der Wert ist: " + MyResult);
28 
29            MyReader.Read();
30            MyConn.Close();
31            MyCmd.Dispose();
32     }
33 }

Um die Oracle- respektive MySQL-Klassen im eigenen Code instanziieren zu können, bedarf es einiger zusätzlicher Namespaces im C#-Programm:

using System.Data;
using MySql.Data.MySqlClient;
using System.Data.OracleClient;

Die Bibliothek »System.Data« dient der abstrakten Verarbeitung von Daten in Form von Zeilen, Spalten, Tabellen und Datasets, während die beiden anderen Module für die entsprechenden Datenbanksysteme zuständig sind. Um diese beiden Klassen hinter einer Fassade zu verbergen, bedarf es einer weiteren Klasse, die selber keinerlei Implementierungen für den Datenbankzugriff beinhaltet (Listing 3).

Listing 3: Implementierung von
Facade

01 public class DbFacade
02   {
03     private MySqlZugriff MySQLClient = new MySqlZugriff();
04     private OraZugriff   OraClient   = new OraZugriff();
05 
06     public string[] LoadFromOra()
07     {
08       return OraClient.OraConnect();
09     }
10 
11     public void LoadFromMy()
12     {
13       MyClient.MyConnect();
14     }
15   }

Das Hauptprogramm in Listing 4 initialisiert zunächst in Zeile 3 die Klasse »DbFacade« und weist sie der Variablen Facade zu. Die folgenden beiden Zeilen führen die hinter Facade verborgenen Objekte aus.

Listing 4: Zugriff per
Facade

01 public static void Main (string[] args)
02 {
03    DbFacade Facade = new DbFacade();
04    Console.WriteLine(Facade.LoadFromOra());
05    Facade.LoadFromMy();
06 }

Über Facade und die beiden öffentlichen Methoden »LoadFromOra« und »LoadFromMy« lassen sich zwei verschiedene Datenbanksysteme kontaktieren, ohne dass der Programmierer mit der Implementierung in Berührung kommt. Nur die Methodennamen liefern Indizien dafür, um welche Datenbanken es sich handelt. Bei einer solchen Vorgehensweise lassen sich anstehende Code-Änderungen unabhängig vom Einsatz im Hauptprogramm programmieren.

Leichtere Wartung

Da die Mechanik von der Anwendung und somit auch von der Benutzerschnittstelle getrennt ist, sind Fehler oder Inkompatibilitäten besser lokalisierbar. Sie befindet sich zudem immer innerhalb der jeweiligen spezialisierten Klasse, da Facade selbst über keine herstellerspezifischen Codebereiche verfügt. Es tritt hier auf in der Form eines Subsystems, das Komplexität verbirgt.

Auch im Falle einer anderen Datenbank, also auch einer neuen Klasse, ist die Erweiterbarkeit gewährleistet, da alle Datenbankobjekte dem gleichen Muster (Pattern) folgen: Sie werden im Private-Bereich der Klasse »DbFacade« initialisiert und über eine öffentliche Methode zur Verfügung gestellt. Dieses Pattern ist nur eines von vielen und zeigt nur einen kleinen Ausschnitt der Möglichkeiten, die sich durch eine konsequente Anwendung wiederkehrender Codemuster verwirklichen lassen. (ofr)

Infos

[1] Design Patterns: [http://en.wikipedia.org/wiki/Design_Patterns]

[2] Facade: [http://c2.com/cgi/wiki?FacadePattern]

[3] Oracle XE: [http://www.oracle.com/technology/products/database/xe]

DIESEN ARTIKEL ALS PDF KAUFEN
EXPRESS-KAUF ALS PDFUmfang: 3 HeftseitenPreis €0,99
(inkl. 19% MwSt.)
LINUX-MAGAZIN KAUFEN
EINZELNE AUSGABE Print-Ausgaben Digitale Ausgaben
ABONNEMENTS Print-Abos Digitales Abo
TABLET & SMARTPHONE APPS Readly Logo
E-Mail Benachrichtigung
Benachrichtige mich zu:
0 Kommentare
Älteste
Neuste Beste Bewertung
Inline Feedbacks
Alle Kommentare anzeigen
Nach oben