Bei den heutigen Datenbank-Management-Systemen (DBMS) kann man sagen, daß Typen, APIs
und Funktionalitäten nicht konstant sind. Weil der DBMS-Markt heftig umkämpft ist,
müssen die Anbieter ständig neue Versionen mit zusätzlichen Fähigkeiten entwickeln.
Der neueste Schub von Aktivitäten um das WorldWideWeb ist ein weiteres Beispiel für den
Druck, SQL-Produkten neue Funktionalität zu verleihen. Das Interesse an der
Internet-Technologie hat Datenbank-Firmen veranlaßt, sich auf universelle
Anbindungsmöglichkeiten und reiche Datentypen zu konzentrieren und HTML an Web-Browser zu
liefern. Gut daran ist, daß das Web Verbindungen fördert. Schlecht ist, daß es eine
Mentalität des "eine Größe paßt für alle" betont, die nicht sehr
realistisch ist, wenn man mit den heutigen Datenbankprodukten programmiert, sogar bei APIs
für multiple Datenbanken.
Nehmen Sie die Situation bei SQL-Datenbanken und Stored Procedures, auch als
persistente SQL-Module bekannt. Prozeduren sind optimierte SQL-Skripte oder Module, die in
der Datenbank gespeichert werden. Sie befinden sich auf dem Server und sind den Clients
zugänglich. Obwohl Stored Procedures wahrscheinlich Bestandteil des zukünftigen
SQL3-Standards sein werden, sind sie im heutigen SQL-Standard nicht definiert. Dies
bedeutet, daß die Unterstützung von Stored Procedures je nach Implementierung des
Anbieters Änderungen und Abweichungen erfahren hat, sogar über Generationen des gleichen
Produkts hinweg. So waren Stored Procedures kein Bestandteil von Watcom SQL 3.2, aber in
den Nachfolgeprodukten Watcom 4.0 und Sybase SQL Anywhere sind sie verfügbar. Wenn Sie
Anwendungen haben, die ursprünglich Watcom 3.2 benutzten, haben Sie wohl mindestens
einmal Ihren Code für SQL-Prozeduren ändern müssen. Dies werden wahrscheinlich nicht
Ihre letzten Änderungen sein; der Wettlauf der SQL-Anbieter, Java als Sprache für Stored
Procedures einzusetzen, hat gerade erst begonnen.
Die Black Box und Multi-Datenbank-APIs
Die Veränderlichkeit von DBMS-Produkten und Versionen bedeutet, daß der Entwickler
ständig seine Datenbankprogramme anpassen muß, um mit neuen Typen, API-Revisionen und
neuen Funktionalitäten Schritt zu halten. Erfahrene Entwickler versuchen oft, den Anteil
des Quellcodes möglichst klein zu halten, der von diesen Änderungen betroffen
ist, indem
sie ihre Programme auf einem Abstraktionsniveau schreiben, das die Datenbank als eine
"Black Box" behandelt.
Die ehrwürdige "Black Box" ist eine Lösung, um mit Unstimmigkeiten und
Implementierungsunterschieden fertig zu werden. Sie ist eine nützliche
Verallgemeinerung,
die den Code isoliert, der von Unterschieden in der Implementierung betroffen
ist; daher
dient sie oft als Lösung für Software, die auf unterschiedlichen Plattformen laufen
muß. Der UNIX-Kernel und die Windows NT-Hardware-Abstraktionsschicht (HAL) sind
klassische Beispiele für das Black Box-Konzept. Ein hoher Anteil des Codes in diesen
Betriebssystemen ist portabel, weil er die hardwarespezifische Logik als eine Black Box
behandelt. Virtuelle Maschinen wie die Java VM sind eine Variation zum Thema; daher sind
Java-Klassen hardwareunabhängig und portabel. Programmierer benutzen JAVA APIs mit einer
virtuellen Maschine, einer Black Box, die ein gleichförmiges Verhalten über die
Plattformen hinweg sichert. (Die Implementierungen von Java sind noch nicht
vollkommen,
aber im Laufe der Zeit wird Java die Black Box-Puristen sicherlich
zufriedenstellen.)
Datenbankprogrammierer sehen Open Database Connectivity (ODBC) und Java Database
Connectivity (JDBC) oft als APIs für virtuelle Datenbanken. JDBC und ODBC arbeiten mit
einer Vielzahl von Datenbanken, und sie sind für eine Vielzahl von Betriebssystemen
verfügbar. Aber obwohl ODBC und JDBC Multi-Datenbank-Produkte und
Multi-Plattform-Produkte sind, mann man sie in der Art und Weise, in der sie das Black
Box-Verhalten implementieren, nicht mit Java gleichsetzen. Der Unterschied zwischen Java
und Datenbank-APIs ist, daß ein Datenbankprogrammierer über Server und Engines hinweg
kein gleichförmiges Verhalten erwarten sollte.
Die Probleme bei der Verwendung von portablem SQL und beim Erstellen vom
Logik, die auf
verschiedenen Plattformen funktioniert, sind der aktuellen Lage bei Web-Browsern und HTML
nicht unähnlich. Sie müssen abwägen zwischen der Benutzung von Standards mit einem
Höchstmaß an Portabilität und der Verwendung von Erweiterungen, die Fähigkeiten auf
Kosten der Portabilität liefern. Ein Vorteil von ODBC und JDBC ist, daß sie
Multi-Datenbank-APIs sind und so Funktionalität enthalten, die es Ihrem Programm
erlaubt,
sich an seine Umgebung anzupassen und eine Auswahl der zu benutzenden Funktionalität zu
treffen.
Außer bei sehr einfachen Anwendungen sollte ein ODBC- oder JDBC-Programm nicht so
geschrieben werden, als ob das DBMS eine Black Box mit definiertem Verhalten
wäre.
Ladbare Datenbanktreiber erlauben es ODBC- und JDBC-Programmen, Verbindungen zu einer
Vielzahl von SQL-Datenbanken aufzubauen, aber keine der beiden APIs garantiert ein
konsistentes Verhalten der Treiber, des Netzwerk-Transports oder der Datenbank-Engine.
ODBC bietet beispielsweise eine Funktion, die als Verbindungsoption (ODBC 2.0) oder
Verbindungsattribut (ODBC 3.0) asynchrone Verarbeitung erlaubt. Asynchrone Verarbeitung
bedeutet, daß die Client-Anwendung den Benutzer nicht blockiert und ihm das
Weiterarbeiten erlaubt, während er auf die Beendigung einer Abfrage wartet. Wenn Sie
diese Option setzen, bedeutet das aber noch nicht, daß Ihr Programm automatisch mit jedem
DBMS und jeder Datenbank, zu der es verbunden ist, im asynchronen Modus
arbeitet. Einige
Netzwerk-Bibliotheken unterstützen keinen asynchronen Modus, und ein Aufruf der
ODBC-Funktion ändert nichts an dieser Einschränkung.
Introspektion und Selbstanpassende Programmierung
Ein Mißverständnis, das ich in meinen Seminaren zur Datenbank-Programmierung zu
korrigieren versuche, ist daß ODBC und JDBC keine Funktionalität
voraussetzen. Die
irrige Erwartung einiger Entwickler ist, daß das DBMS einem Programm, welches ODBC oder
JDBC benutzt, ein bestimmtes Verhalten garantiert. Zum Beispiel definiert ODBC Konstanten
für 19 Datentypen, und manche Entwickler nehmen an, daß sie diese Typen mit jedem
ODBC-Treiber und DBMS verwenden können. In Wirklichkeit muß ein ODBC-Treiber nur einen
Datentyp 'Character' unterstützen, um die Minimalanforderung der ODBC-Grammatik zu
erfüllen.
Statt Funktionalität vorauszusetzen, berichten die ODBC und JDBC APIs, welche
Funktionalität vom DBMS und seinem Treiber verfügbar ist. ODBC enthält Funktionen und
JDBC Methoden, die Ihrem Programm die Möglichkeit geben, nachdem Sie die Verbindung zur
Datenbank hergestellt haben, zu bestimmen, was deren Fähigkeiten sind. Entwickler können
Abfragetechniken zur Laufzeit (Introspektion) benutzen, um Programme zu
schreiben, die
sich an die im Moment verfügbaren SQL-Dialekte, Typen, API-Funktionen und andere
Funktionalität anpassen. Weil Ihr Programm dies zur Laufzeit und nicht bei der
Übersetzung tut, benötigt es möglicherweise keine Änderungen, um mit neuen
DBMS-Versionen zu arbeiten. Es gibt keine Garantie, daß Sie Ihr Programm völlig von
Änderungen des DBMS isolieren können, aber für viele Änderungen müssen Sie keinen
neuen Code schreiben. Selbstanpassende Programmierung ist auch nützlich für
Anwendungen,
die mit einer heterogenen Mischung aus Datenbanken und Treibern arbeiten. Crystal Reports
und Microsoft Access sind Beispiele für Anwendungen, die selbstanpassende Logik
benutzen,
um als Client für die verschiedensten SQL-Server funktionieren zu können.
ODBC ist eine Aufruf-Schnittstelle (Call Level Interface), die Introspektion mit
Funktionen unterstützt, die über die Funktionalitäten (SQLGetInfo), Typen (SQLGetTypeInfo),
Prozeduren (SQLProcedures), die API (SQLGetFunctions) und andere
Eigenschaften der Datenbank berichten. JDBC besteht aus einer Schnittstelle und Klassen,
mit denen der Entwickler Objekte benutzen kann, während er auf die SQL-Datenbank
zugreift. Um mit diesen beiden Schnittstellen auf Daten zuzugreifen, benutzt ein
Entwickler SQL-Befehle und arbeitet mit Ergebnismengen. Es dürfte keine Überraschung
sein, daß JDBC Objekte wie Verbindungen (java.sql.connection) und Befehle
(java.sql.statement) benutzt. ODBC bietet keine derartigen Objekte und Klassen an, aber es
benutzt Handles um Verbindungen, Befehle und die dazugehörigen Informationen zu
verwalten.
JDBC enthält Klassen, die Metadaten oder Kataloginformationen über die Datenbank und
die Abfrageergebnisse liefern. Die Datenbank-Metadaten-Klasse (java.sql.DatabaseMetaData)
verkapselt einen Großteil der Daten, die ODBC-Funktionsaufrufe wie SQLGetTypeInfo
und SQLGetInfo zurückgeben. Einfach ausgedrückt ist dies die Klasse, die viele
Informationen darüber liefert, wie sich ein DBMS vom anderen unterscheidet.
JDBC-Metadaten-Methoden können Ganzzahlen, Zeichenketten, Wahrheitswerte und andere Typen
zurückgeben. JDBC-Methoden können wie ODBC-Funktionen Metadaten als Ergebnismengen
zurückgeben. ODBC- und JDBC-Programme benutzen oft ein ähnliches
Programmierungs-Paradigma, um Daten-Anfragen und Metadaten-Anfragen zu
verarbeiten.
Datenbank-Metadaten
Entwickler, die Multi-Datenbank-Code schreiben, sollten nicht übersehen, daß Typen
nicht konstant sind. Dies ist eine Binsenweisheit, wenn man Produkte oder auch
verschiedene Versionen eines Produkts miteinander vergleicht. Wir sehen also, daß unsere
Programme veralten können, soweit sie eine fest codierte Typenliste verwenden. Anstatt
Programme mit statischen Typenlisten zu schreiben, sollten Sie erwägen, ODBC und
JDBC-APIs zu verwenden, die Typ-Informationen zur Laufzeit ermitteln können. Die
JDBC-DatabaseMetaData-Klasse liefert Typ-Informationen in ähnlicher Weise wie die
ODBC-Funktion SQLGetTypeInfo. Beide APIs geben Typ-Informationen als Ergebnismengen
zurück, deren Spalten die verschiedenen Informationen über die Typen
darstellen. Eine
der wichtigsten Spalten ist der Name des Typs (TYPE_NAME), denn dies ist der Name, den Sie
in SQL-Befehlen benutzen müssen. ODBC und JDBC verstehen eine definierte Liste von
SQL-Typen, die Zeichenketten, Binärdaten, Datumswerte, Dezimalzahlen, Gleitkommawerte mit
einfacher und doppelter Genauigkeit, Ganzzahlen, Uhrzeiten und anderes umfaßt. Die
Ergebnismenge gibt an, welche dieser SQL-Datentypen verfügbar ist, nachdem Ihr Programm
eine Verbindung zu dem betrachteten Treiber aufgebaut hat. Die Ergebnismenge gibt die
maximale Präzision des Datentyps zurück, seine minimale und maximale Skalierung und ob
Skalierung für diesen Typ überhaupt anwendbar ist. Sie zeigt auch an, ob der Datentyp
einen Nullwert akzeptiert, ob er Groß- und Kleinschreibung beachtet, ob er ein
Währungsformat hat, und wie der Typ in SQL WHERE-Sätzen benutzt werden kann. Andere
Informationen sagen aus, ob der Typ die Autoincrement-Methode benutzt, ob er ein
Vorzeichen enthält oder nicht, oder ob Vorzeichen für diesen Typ gar nicht anwendbar
sind.
Das Konzept der Typ-Information durch eine API zur Laufzeit ist klar, aber Sie fragen
sich vielleicht, warum es notwendig oder sogar nützlich ist. Es gibt verschiedene
Gründe, die unter die Überschriften Flexibilität und Vermeidung von Veralterung fallen.
Datenbankprodukte ändern sich, und neue Versionen eines Produkts erweitern oft die reiche
Auswahl an Typen. Wenn Sie Konstanten benutzen, um Typen zur Übersetzungszeit zu
definieren, werden Sie Ihren Code wahrscheinlich neu übersetzen und linken müssen, wenn
eine neue Version des DBMS die Typenauswahl erweitert hat. Ähnlich verhält es sich, wenn
Sie Ihren Code mit heterogenen Datenbanken einsetzen wollen, die eine Vielfalt von Typen
unterstützen.
En anderes Szenario ist die Notwendigkeit, ein Programm zu schreiben, das Tabellen für
verschiedene Datenbanken erzeugt, oder das die Struktur der zu erzeugenden Tabelle vom
Benutzer abfragt. Um den SQL TABLE-Befehl aufzubauen, braucht Ihr Programm den
Namen, den
es für den Typ jeder Spalte in der Tabelle benutzen muß. Der Typ-Name muß für den
SQL-Dialekt der Datenbank gültig sein, mit der das Programm verbunden ist. Eine typische
Technik ist die Anzeige einer Listbox, die es dem Benutzer erlaubt, für jede Spalte den
Typ auszuwählen. Um sicherzustellen, daß diese angezeigte Typenliste auch aktuell
ist,
sollten Sie diese Liste zur Laufzeit aufbauen statt die Typen in Ihrem Code zu
definieren.
Bei der dynamischen Erzeugung einer derartigen Typliste ist das Verständnis der
ODBC-Funktion SQLGetTypeInfo oder der JDBC-Methode getTypeInfo
nützlich.
Beispiel-Programm für JDBC-Typ-Information
Listing 2 ist ein Beispiel-Programm (TYPEINFO.JAVA), das die Benutzung der getTypeInfo-Methode
der JDBC DatabaseMetaData-Klasse demonstriert. Weil TYPEINFO ein Java-Programm ist,
enthält es eine Hauptklasse. Sie führen TYPEINFO auf einer virtuellen Java-Maschine aus,
im Gegensatz zu einem Applet, das ein Browser ausführt. Die Befehlszeile, die ich
benutze, um die Java VM zur Ausführung von TYPEINFO aufzurufen, ist :
java -classpath E:\java\jdbc\classes;E:\lib;E:\lib\java\lib\classes.zip; GetTypes
TYPEINFO benutzt die JDBC-ODBC-Brücke, um sich zu ODBC-Datenquellen zu
verbinden, und
erzeugt dann eine Ergebnismenge für die Metadaten der Datenbank. Es durchläuft dann eine
Schleife, in der es zeilenweise Informationen über die Typen holt, die der Treiber
unterstützt, mit dem die Verbindung zu dem URL jdbc:odbc:SSPer aufgebaut
wurde. Wenn Sie
TYPEINFO ausführen, berichtet es die Namen und SQL-Typen, die eine Datenbank
bereitstellt. TYPEINFO verbindet zu einer ODBC-Datenquelle (SSPer), die auf meinem
Netzwerk so konfiguriert wurde, daß sie eine Microsoft SQL Server-Datenbank
benutzt. Wenn
Sie TYPEINFO ausführen und zu einem SQL-Server verbinden, werden Sie normalerweise eine
längere Liste von Typen sehen als von einem Desktop-Produkt wie dBase. Die Ausführung
von TYPEINFO, verbunden zu SQL Server, erzeugt eine Typenliste, die Bit, Image, Timestamp,
DateTime und Währung als Datentypen enthält.
Eine unvollkommene Black Box
In einer perfekten Welt funktioniert die Black-Box-Technik wie eine
Steckdose. Sie
stecken den Stecker rein, und Ihr Programm arbeitet, ohne daß es zusätzliche Intelligenz
benötigt. Wenn Sie Lösungen brauchen, um interoperable SQL-Programme zu
schreiben,
werden Sie finden, daß ODBC und JDBC nützliche Technologien sind. Sie erfüllen nicht
hundertprozentig, was die Black Box verspricht, und sie erzeugen keinen
Black-Box-Effekt,
der sich mit den Java APIs und der Java VM vergleichen ließe. Sie werden jedoch
finden,
daß ODBC und JDBC Funktionen bzw. Methoden enthalten, die beim Schreiben von portablem
Datenbankcode nützlich sind.
Copyright © 1997, Ken North
Übersetzung aus dem Amerikanischen durch Johannes
M. Becher
Email-Adresse joh@nnes.de
WWW-Adresse http://problemloeser.de/start.htm
SQLSummit
Web Services Summit
GridSummit