der Black Box
Home Up XQuery Modeling Data Mining Optimization Trends SOX der Black Box BlackBox SQL Trees

 



Fast, reliable data access for ODBC, JDBC, ADO.NET and XML
WSSC 2008: An event dedicated to SOA and Web Services Security
Got SOX compliance?
Movielink Logo 88x31
Business Intelligence with R&R ReportWorks
IBM eserver xSeries 306m 8849 - P4 3.4 GHz
Memory
PROLIANT BL20P G3 XEON 3.6G 2P
iTunes Logo 88x31-1

 

ODBC, JDBC und die Suche nach der Black Box
von Ken North

 

Dies ist eine deutsche Übersetzung aus der Database Developer-Kolumne in Dr. Dobb's Sourcebook. Der Java-Quellcode ist auf den Web-Seiten von Dr. Dobb's verfügbar. Der Text und der Code sind Auszüge aus Database Magic with Ken North (Prentice Hall PTR, 0-13-647199-4).

Während ich dies schreibe, ist offensichtlich immer noch manches richtig, das schon zu Beginn meiner Karriere galt. Donald Knuth schreibt an einem Band von The Art of Computer Programming. George Foreman ist auf einem Spitzenpltz der Rangliste im Schwergewicht. Mick Jagger, Eric Clapton und Tina Turner treten vor riesigen Zuschauermengen auf. IBM ist bei weitem die größte Firma in der Computerindustrie.

"Plus ça change, plus c'est la même chose."

Sollten wir daraus schließen, daß die Begriffe, die wir zur Beschreibung der Datenbankprogrammierung benutzen, immer mit 'Konstante' anfangen ? Die Franzosen sagen: "Je mehr sich die Dinge ändern, desto mehr bleiben sie gleich." Ist das heute eine vernünftige Einstellung für einen Datenbankentwickler ?

Wenn Sie SQL-Programme in C oder C++ geschrieben haben, dann sind Sie es wahrscheinlich gewöhnt, Header-Dateien zu benutzen und Konstanten einzubinden. Beispiele sind SQLTYPES.H, das bei Informix ESQL benutzt wird, oder CSTYPES.H bei der Sybase Open Client NT-Library. Zur Programmierung mit dem Oracle Call Interface haben Sie wahrscheinlich eine Header-Datei mit Prototypen für oopen(), oclose(), ologon() und andere Funktionen. Bleibt die Information konstant, so daß Programme, die mit diesen Header-Dateien übersetzt wurden, für eine weitere Generation gut sind? Betrachten Sie die Header-Datei in Listing 1.


Listing 1 KONSTANTE.H


char *AktuellesKnuthProjekt = "The Art of Computer Programming";

#define ClaptonIstEinStar True

#define ForemanIstDerChamp True

#define NeuerFordMustangKostet 2368.


Wenn wir nur auf die ersten drei Zeilen schauen, dann scheint es, als ob dieser Header heute noch genauso gültig ist wie vor Jahrzehnten. Die letzte Zeile erinnert uns aber daran, daß nicht alles konstant ist, und daß wir deshalb jedes Programm, das diesen Header benutzt, neu übersetzen müssen. Ist dieses Beispiel repräsentativ für die Situation, die wir beim Schreiben von Programmen mit Datenbankzugriff vorfinden? Betrachten wir einige Beispiele. Ein Header, der Datentypen von Paradox 3.0 definierte, ist für die heutige Version von Paradox überholt. Ein Programm, das im Quellcode eine Liste der Funktionen in der ODBC-API (Application Programming Interface = Anwendungsprogramm-Schnittstelle) aufzählt, wird in dem Maße überholt sein, in dem ODBC 3.0 frühere Versionen ersetzt.

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 WWW-Adresse http://problemloeser.de/start.htm


SQLSummit           GridSummit