Galileo Computing < openbook >
Galileo Computing - Professionelle Buecher. Auch fuer Einsteiger.
Galileo Computing - Professionelle Buecher. Auch fuer Einsteiger.


Java ist auch eine Insel von Christian Ullenboom
Buch: Java ist auch eine Insel (Galileo Computing)
gp Kapitel 20 Datenbankmanagement mit JDBC
gp 20.1 Das relationale Modell
gp 20.2 JDBC: der Zugriff auf Datenbanken über Java
gp 20.3 Die Rolle von SQL
gp 20.3.1 Ein Rundgang durch SQL-Anfragen
gp 20.3.2 Datenabfrage mit der Data Query Language (DQL)
gp 20.3.3 Tabellen anlegen mit der Data Definition Language (DDL)
gp 20.4 Datenbanktreiber für den Zugriff
gp 20.4.1 Treibertypen
gp 20.5 Datenbanken und ihre Treiber
gp 20.5.1 Die freie Datenbank MySQL
gp 20.5.2 JDBC-Treiber für MySQL: MySQL Connector/J
gp 20.5.3 Die Datenbank Microsoft Access
gp 20.5.4 Ein Typ-4-Treiber für den Microsoft SQL Server 2000
gp 20.5.5 Die JDBC-ODBC-Bridge
gp 20.5.6 ODBC einrichten und Access damit verwenden
gp 20.5.7 Oracle9i Enterprise Edition
gp 20.5.8 JDBC-Treiber für mobile Endgeräte
gp 20.6 Eine Beispielabfrage
gp 20.7 Mit Java an eine Datenbank andocken
gp 20.7.1 Der Treibermanager
gp 20.7.2 Eine Aufzählung aller Treiber
gp 20.7.3 Log-Informationen
gp 20.7.4 Den Treiber laden
gp 20.7.5 Verbindung zur Datenbank
gp 20.8 Datenbankabfragen
gp 20.8.1 Abfragen über das Statement-Objekt
gp 20.8.2 Ergebnisse einer Abfrage in ResultSet
gp 20.8.3 Unicode in der Spalte korrekt auslesen
gp 20.8.4 wasNull() bei ResultSet
gp 20.8.5 Wie viele Zeilen hat ein ResultSet?
gp 20.9 Java und SQL-Datentypen
gp 20.9.1 Die getXXX()-Methoden
gp 20.10 Transaktionen
gp 20.11 Elemente einer Datenbank hinzufügen und aktualisieren
gp 20.11.1 Batch-Updates
gp 20.12 Vorbereitete Anweisungen (Prepared Statements)
gp 20.12.1 PreparedStatement-Objekte vorbereiten
gp 20.12.2 Werte für die Platzhalter eines PreparedStatement
gp 20.13 Metadaten
gp 20.13.1 Metadaten über die Tabelle
gp 20.13.2 Informationen über die Datenbank
gp 20.14 Die Ausnahmen bei JDBC
gp 20.15 Java Data Objects (JDO)
gp 20.16 XML-Datenbanken
gp 20.16.1 Apache Xindice
gp 20.16.2 eXist und Weitere


Galileo Computing

20.7 Mit Java an eine Datenbank andockendowntop

Die Verbindung zu einer Datenbank wird über die Klasse DriverManager und die Schnittstelle Connection aufgebaut. Alle verwendeten Pakete liegen unter java.sql.*. Vor der Ausführung der JDBC-Befehle muss ein passender Datenbanktreiber geladen werden. In unserem Beispiel verwenden wir die von JavaSoft mitgelieferte JDBC-ODBC-Bridge, deren Klasse unter dem Namen JdbcOdbcDriver verfügbar ist. Damit können wir uns zu allen angemeldeten ODBC-Datenquellen verbinden.


Galileo Computing

20.7.1 Der Treibermanagerdowntop

Alle Datenbanktreiber müssen an einer Stelle gesammelt werden, dem Treibermanager. Dazu dient eine besondere Java-Klasse, die Klasse DriverManager. Wie wir später sehen werden, bietet der Treibermanager eine Methode getConnection() an, mit der wir eine Verbindung zur Datenbank aufbauen können.

Abbildung
Hier klicken, um das Bild zu Vergrößern


Galileo Computing

20.7.2 Eine Aufzählung aller Treiberdowntop

Die statische Methode getDrivers() der Klasse DriverManager liefert eine Aufzählung der angemeldeten Treiber. Alle Methoden der Klasse sind statisch, da sich ein Exemplar dieser Klasse nicht erzeugen lässt; der Konstruktor ist privat. Ein normales Programm hat in der Regel keinen angemeldeten Datenbanktreiber. Eine Aufzählung bekommen wir durch folgende Zeilen:

for ( Enumeration e = DriverManager.getDrivers(); e.hasMoreElements(); )
  System.out.println( e.nextElement().getClass().getName() );

Die Elemente, die durch die Enumeration ausgelesen werden, sind Treiber-Objekte. Jeder Datenbanktreiber ist als Treiber-Objekt implementiert. Da Driver aber eine Schnittstelle ist, gibt es keine sinnvolle toString()-Methode und wir bekommen den Klassennamen und einen Hash-Wert. Schöner ist der Klassenname, den wir über ein Class-Objekt erfragen können. getClass() liefert das Klassen-Objekt und getName() den Namen. Ohne geladenen Treiber bekommen wir keine Ausgabe. Laden wir den JDBC-ODBC-Treiber, etwa durch

Class.forName( "sun.jdbc.odbc.JdbcOdbcDriver" );

dann ist die Ausgabe:

sun.jdbc.odbc.JdbcOdbcDriver

Galileo Computing

20.7.3 Log-Informationendowntop

Zu Testzwecken bietet es sich an, Informationen des Treibers und der Datenbank in einen speziellen Ausgabekanal zu schreiben. Wir können die Log-Informationen so umlenken, dass sie in den Standardausgabestrom geschrieben werden. Dazu dient die statische Methode setLogWriter(). Sie bekommt einen PrintWriter als Parameter. Vor Java 1.2 hieß die Methode noch setLogStream(), diese nahm als Parameter einen PrintStream. Sie ist nun veraltet.

Folgende Zeile gibt alle Informationen auf dem Bildschirm aus, die in das Logbuch geschrieben werden:

DriverManager.setLogWriter( new PrintWriter(System.out) );

Ladevorgang der Treiber protokolliert ausgeben

Eine solche Ausgabe ist hilfreich, da interessante Aussagen über die Funktionsweise der Treiber auf diese Art offenbar werden. Zur Set-Methode existiert die passende getLogWriter()-Methode, die den PrintWriter zurückgibt. Eine Anfrage an getLogWriter() gibt null zurück, was verrät, dass standardmäßig keine Ausgabe stattfindet. Es wird also keine versteckte Datei erzeugt.

Testen wir die Ausgabe, die die beiden Programmzeilen erzeugen:

DriverManager.setLogWriter( new PrintWriter(System.out) );
Class.forName( "sun.jdbc.odbc.JdbcOdbcDriver" );

Zunächst setzen wir die Ausgabe auf den Standardausgabekanal. Dann laden wir die Treiberklasse für die JDBC-ODBC-Brücke. An dieser Stelle greifen wir schon etwas vor. Die Ausgabe zeigt den eingetragenen Treiber.

DriverManager.initialize: jdbc.drivers = null
JDBC DriverManager initialized
registerDriver: driver[className=sun.jdbc.odbc.JdbcOdbcDriver,\
sun.jdbc.odbc.JdbcOdbcDriver@1fcc69]

Die Methode setLogWriter() ist die erste Methode, die die Klasse DriverManager benutzt. Daher bekommt der Klassenlader die Aufgabe, die Klasse DriverManager zu laden. setLogWriter() speichert dann das PrintWriter-Objekt in einer privaten Variablen und macht sonst nichts.

Erst das Laden eines Treibers führt zum Aufruf der statischen initialize()-Methode. Sie führt die private Methode loadInitialDrivers() aus, die zur ersten Ausgabezeile führt. Hier sind noch keine Treiber angemeldet, da in den Eigenschaften »jdbc.drivers« nichts eingetragen ist. Diese Eigenschaft wird in der Regel dann gesetzt, wenn von außen über den Schalter »-D« eine Klasse angesprochen wird. Nach dem Suchen in den Eigenschaften folgt die Ausgabe »JDBC DriverManager initialized«. Nun hat der Treiber die DriverManager-Klasse hochgefahren, und der Treibermanager kann den Treiber anmelden. Er ist vom Typ Driver. Dieser wird zusammen mit dem zugehörigen Class-Objekt und einem Namen in der internen Klasse DriverInfo in einem internen Vector gespeichert. Die Ausgabe »registerDriver:[...]« stammt von der Anmeldung des Treibers. Wir sehen genau die Informationen, die in der Klasse DriverInfo gespeichert sind. Die Ausgabe wird auch von toString() von DriverInfo generiert.

Nicht nur Treiber und SQL-Klassen nutzen den Log-Stream, auch wir können Zeichenketten ausgeben. Dazu dient die statische Methode println(), die als Parameter nur einen String annimmt. println() ist so implementiert, dass bei einem nicht gesetzten Log-Stream die Ausgabe unterbleibt.


Galileo Computing

20.7.4 Den Treiber ladendowntop

Der Datenbanktreiber ist eine ganz normale Java-Klasse, die sich bei einem Treibermanager automatisch anmeldet. Unsere Aufgabe ist es nur, ein Treiber-Objekt einmal zu erzeugen. Um eine Klasse zur Laufzeit zu laden und so ein Laufzeit-Objekt zu erstellen, gibt es mehrere Möglichkeiten. Eine davon nutzt die native statische Methode forName() der Klasse Class. Die Syntax für das Laden der JDBC-ODBC-Bridge lautet somit:

Class.forName( "sun.jdbc.odbc.JdbcOdbcDriver" );

Um möglichst unabhängig zu bleiben, sollte die Klasse auch nicht hart einkodiert werden. Besser ist es, den Klassennamen in eine Property zu schreiben. Da wir die Klasse nur laden, aber die Referenz auf den Klassen-Deskriptor nicht benötigen, belassen wir es bei einem Aufruf und beachten den Rückgabewert nicht. Diese Methode löst eine ClassNotFoundException aus, falls die Klasse nicht gefunden wurde, der Treiber also nicht geladen werden konnte.1


Datenbank Klassenname für den JDBC-Treiber
Oracle oracle.jdbc.driver.OracleDriver
mSQL COM.imaginary.sql.msql.MsqlDriver
MySQL com.mysql.jdbc.Driver6
IBM DB2 COM.ibm.db2.jdbc.net.DB2Driver
Borland JDataStore com.borland.datastore.jdbc.DataStoreDriver
Borland Interbase interbase.interclient.Driver
Adabas D de.sag.jdbc.adabasd.ADriver
SYBASE ASE com.sybase.jdbc.SybDriver
IDS Server ids.sql.IDSDriver

Die Klasse muss nicht zwingend zur Laufzeit geladen werden. Sie kann auch in der Kommandozeile über den Schalter »-D« eingebunden werden. Dazu setzen wir mit der Eigenschaft jdbc.drivers einen Datenbanktreiber fest.

java -Djdbc.drivers=sun.jdbc.odbc.JdbcOdbcDriver <Javaklasse>

final class java.lang.Class
implements Serializable

gp static Class forName( String clazz ) throws ClassNotFoundException
Sucht, lädt und bindet die Klasse mit dem qualifizierten Namen clazz ins Laufzeitsystem ein. Es wird ein Class-Objekt zurückgegeben, falls die Klasse geladen werden kann, andernfalls wird dies mit einer ClassNotFoundException quittiert.

Hinweis In einigen Programmlistings findet sich die Zeile zum Laden des Treibers, die gleich mit newInstance() ein Objekt des Treibers erzeugt.
Class.forName( "org.gjt.mm.mysql.Driver" ).newInstance();
Das ist im Prinzip nicht nötig, denn Class.forName() sollte die Klasse laden. Es gibt jedoch virtuelle Maschinen in Applets, die das Klassenladen aus Geschwindigkeitsgründen vermeiden und dann keine Klassendatei vom Server beziehen. In solchen Fällen muss der Laufzeitumgebung mit dem Bilden eines Exemplars explizit der Hinweis gegeben werden, dass die Treiber-.class-Datei nötig ist, damit sich der Treiber beim Treibermanager anmelden kann.


Galileo Computing

20.7.5 Verbindung zur Datenbanktoptop

Nach dem Laden des Treibers können wir eine Verbindung zur Datenbank mit Hilfe des Connection-Objekts aufbauen, welches von DriverManager.getConnection() zurückgegeben wird. Eine Verbindung wird mit speziellen Optionen parametrisiert, unter anderem mit dem Treiber, der die Datenquelle anspricht.

Die Datenquelle angeben

Alle Datenquellen sind durch eine besondere URL qualifiziert, die folgendes Format besitzt:

jdbc:Subprotokoll:Datenquellenname

Datenbank Subprotokoll Beispiel
ODBC-Datenquellen odbc jdbc:odbc:Pflanzen
mSQL msql jdbc:msql://host:1234/database
Interbase interbase jdbc:interbase://localhost/dabase.gdb
Oracle Thin oracle:thin jdbc:oracle:thin:@host:1243:database
IBM DB2 db2 jdbc:db2://database
Sybase sybase:Tds jdbc:sybase:Tds:host:1234/database
MySQL mysql jdbc:mysql://host/database

Verbindung aufnehmen

Die getConnection()-Methode liefert nun ein Connection-Objekt, das mit der Quelle verbunden ist. Die nachfolgende Anweisung verbindet uns mit einer Datenbank, die den Namen Pflanzen trägt. Diesen Namen haben wir im ODBC-Datenquellen-Administrator festgelegt, und er hat nichts mit dem Dateinamen zu tun.

con = DriverManager.getConnection( "jdbc:odbc:Pflanzen",
                                   "user",
                                   "passwd" );

Die Methode getConnection() erwartet bis zu drei Parameter: Die URL der Datenbank, zu der die Verbindung aufgenommen werden soll, ist der Pflichtparameter. Der Anmeldename und das Passwort sind optional. Der Benutzername und das Passwort können auch leere Strings ("") sein. Dieses Vorgehen findet bei Textdateien, die als ODBC-Quellen eingesetzt werden, Verwendung, da Texte keine solchen Attribute besitzen. Meldet getConnection() keinen Fehler, so liefert sie uns eine geöffnete Datenbankverbindung.

Abbildung
Hier klicken, um das Bild zu Vergrößern


class java.sql.DriverManager

gp static Connection getConnection( String url, Properties info ) throws SQLException
Versucht eine Verbindung zur Datenbank aufzubauen. Die Klasse DriverManager sucht dabei einen passenden Treiber aus der Liste der registrierten JDBC-Treiber für die Datenbank. Im Properties-Objekt sollten die Felder »user« und »password« vorhanden sein.
gp static Connection getConnection( String url, String user, String password )
throws SQLException

Versucht eine Verbindung zur Datenbank aufzubauen. user und password werden für die Verbindung zur Datenbank verwendet.
gp static Connection getConnection( String url ) throws SQLException
Versucht eine Verbindung zur Datenbank aufzubauen.

Wie der Treiber gefunden wird

Es lohnt sich, einmal hinter die Kulissen der Methode getConnection() zu sehen. Das DriverManager-Objekt wird veranlasst, die Verbindung zu öffnen. Dabei versucht es, einen passenden Treiber aus der Liste der JDBC-Treiber auszuwählen. Sein Treiber verwaltet die Klasse DriverManager in einem privaten Objekt DriverInfo. Dieses enthält ein Treiber-Objekt (Driver), ein Objekt (securityContext) und den Klassennamen (className).

Während getConnection() die Liste (intern als Vector implementiert) der DriverInfo-Objekte abgeht, versucht dieser, sich über die connect()-Methode anzumelden. Merkt der Treiber, dass er mit der URL nicht viel anfangen kann, gibt er null zurück, und getConnection() versucht es mit dem nächsten Treiber. Ging alles daneben und keiner der angemeldeten Treiber konnte etwas mit dem Subprotokoll anfangen, bekommen wir eine SQLException("No suitable driver", "08001").

Verbindung beenden

Die Klasse DriverManager besitzt keine close()-Methode, wie wir erwarten würden. Vielmehr kümmert sich das Connection-Objekt selbst um die Schließung. Würde der Garbage-Collector also das Objekt von der Halde räumen, schließt er automatisch die Verbindung. Wollen wir selbst das Ende der Verbindung herbeiführen, rufen wir

con.close();

auf, und die Verbindung wird beendet. Auch hier kann eine SQLException auftauchen.


interface java.sql.Connection

gp void close() throws SQLException
Schließt die Verbindung zur Datenbank.

Wartezeit einstellen

Wenn wir uns mit der Datenbank verbinden, lässt sich noch eine Wartezeit in Sekunden einstellen, die angibt, wie lange der Treiber für die Verbindung mit der Datenbank warten darf. Gesetzt wird dieser Wert mit setLoginTimeout() und entsprechend ausgelesen mit getLoginTimeout(). Standardmäßig ist dieser Wert 0.


class java.sql.DriverManager

gp static void setLoginTimeout( int seconds )
Setzt die Zeit, die maximal gewartet wird, wenn der Treiber sich mit einer Datenbank verbindet.
gp static int getLoginTimeout()
Liefert die Wartezeit in Sekunden.





1 Früher org.gjt.mm.mysql.Driver





Copyright (c) Galileo Press GmbH 2004
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das <openbook> denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.


[Galileo Computing]

Galileo Press GmbH, Gartenstraße 24, 53229 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, info@galileo-press.de