20.7 Mit Java an eine Datenbank andocken
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.
20.7.1 Der Treibermanager
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.
Hier klicken, um das Bild zu Vergrößern
20.7.2 Eine Aufzählung aller Treiber
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
20.7.3 Log-Informationen
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.
20.7.4 Den Treiber laden
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
|
|
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.
|
20.7.5 Verbindung zur Datenbank
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.
Hier klicken, um das Bild zu Vergrößern
class java.sql.DriverManager
|
|
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. |
|
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. |
|
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
|
|
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
|
|
static void setLoginTimeout( int seconds )
Setzt die Zeit, die maximal gewartet wird, wenn der Treiber sich mit einer Datenbank verbindet. |
|
static int getLoginTimeout()
Liefert die Wartezeit in Sekunden. |
1 Früher org.gjt.mm.mysql.Driver
|