Galileo Computing < openbook > Galileo Computing - Professionelle Bücher. Auch für Einsteiger.
Professionelle Bücher. Auch für Einsteiger

 << zurück
Java ist auch eine Insel von Christian Ullenboom
Programmieren für die Java 2-Plattform in der Version 5
Java ist auch eine Insel

Java ist auch eine Insel
5., akt. und erw. Auflage
1454 S., mit CD, 49,90 Euro
Galileo Computing
ISBN 3-89842-747-1
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 Derby
    gp 20.5.2 MySQL
    gp 20.5.3 Microsoft Access
    gp 20.5.4 Ein Typ-4-Treiber für den Microsoft SQL Server 2000
    gp 20.5.5 Oracle10g
    gp 20.5.6 Eclipse-Plugins zum Durchschauen von Datenbanken
  gp 20.6 Eine Beispielabfrage
  gp 20.7 Mit Java an eine Datenbank andocken
    gp 20.7.1 Der Treibermanager
    gp 20.7.2 Den Treiber laden
    gp 20.7.3 Eine Aufzählung aller Treiber
    gp 20.7.4 Log-Informationen
    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 Java und SQL-Datentypen
    gp 20.8.4 Unicode in der Spalte korrekt auslesen
    gp 20.8.5 wasNull() bei ResultSet
    gp 20.8.6 Wie viele Zeilen hat ein ResultSet?
  gp 20.9 Die Ausnahmen bei JDBC
  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 Die LOBs (Large Objects)
    gp 20.13.1 Einen BLOB besorgen
  gp 20.14 Die SQL3-Datentypen ARRAY, STRUCT und REF
  gp 20.15 Metadaten
    gp 20.15.1 Metadaten über die Tabelle
    gp 20.15.2 Informationen über die Datenbank
  gp 20.16 DataSource
    gp 20.16.1 Die Schnittstelle DataSource


Galileo Computing

20.7 Mit Java an eine Datenbank andockedowntop

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.


Galileo Computing

20.7.1 Der Treibermanager  downtop

Alle Datenbanktreiber müssen an einer Stelle, dem Treibermanager, gesammelt werden. Dazu dient eine besondere Java-Klasse, die Klasse DriverManager. Alle Methoden der Klasse sind statisch, da sich ein Exemplar dieser Klasse nicht erzeugen lässt; der Konstruktor ist privat. Die wichtigste Methode des Treibermanagers ist getConnection(), mit der wir eine Verbindung zur Datenbank aufbauen können. Es lassen sich aber auch alle angemeldeten Treiber erfragen.

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


Galileo Computing

20.7.2 Den Treiber laden  downtop

Der Datenbanktreiber ist Java-Klasse, die sich beim Einbinden in die JVM automatisch beim Treibermanager anmeldet. Unsere erste Aufgabe ist es, die Treiber-Klasse zu laden, sodass das Treiber-Objekt gebildet und beim Manager eingetragen werden kann. Zwei Möglichkeiten sind populär. Die erste: Der Treiber wird in der Kommandozeile über den Schalter »-D« eingebunden. Dazu setzen wir mit der Eigenschaft jdbc.drivers einen Datenbanktreiber fest.6

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

Die Property jdbc.drivers lässt sich auch in einer externen Datei definieren.

Die zweite Möglichkeit ist die Funktion Class.forName(), die eine Klasse lädt.


final class java.lang.  Class<T>
  implements Serializable, GenericDeclaration, Type, AnnotatedElement

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

Die Programmzeilen für das Laden der JDBC-ODBC-Bridge und der Klasse com.ibm.db2. jcc.DB2Driver sind somit:

Listing 20.5   com/javatutor/insel/jdbc/DriverManagerDemo.java, Ausschnitt

try
{
  Class.forName( DatabaseConstants.DRIVER );
  Class.forName( "sun.jdbc.odbc.JdbcOdbcDriver" );
}
catch ( ClassNotFoundException e )
{
  // OO! Treiber konnte nicht geladen werden.
  e.printStackTrace();
}

Um die Bindung zu einer Datenbank lose zu halten, haben wir den Klassennamen nicht hart einkodiert, sondern die Klasse DatabaseConstants geschrieben. (Noch besser ist es, aus dem Namensdienst eine DataSource zu nehmen.) 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.


Datenbank Klassenname für den JDBC-Treiber
DB2/Derby com.ibm.db2.jcc.DB2Driver
Oracle oracle.jdbc.driver.OracleDriver
mSQL COM.imaginary.sql.msql.MsqlDriver
MySQL com.mysql.jdbc.Driver
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


Hinweis   In einigen Programmlistings findet sich die Zeile zum Laden des Treibers, die gleich mit newInstance() ein Objekt des Treibers erzeugt.
Class.forName("com.ibm.db2.jcc.DB2Driver").newInstance();
Das ist im Prinzip nicht nötig, denn Class.forName() sollte die Klasse laden. Es gibt jedoch virtuelle Maschinen, die das Klassenladen aus Geschwindigkeitsgründen vermeiden und dann keine Klassendatei einladen. 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.3 Eine Aufzählung aller Treiber  downtop

Die statische Funktion getDrivers() der Klasse DriverManager liefert eine Aufzählung der angemeldeten Treiber. Die folgenden Zeilen geben einfach den Klassennamen aus – die Treiber implementieren nicht unbedingt eine sinnvolle toString()-Methode, sodass wir uns mit dem Klassennamen begnügen.

Listing 20.6   com/javatutor/insel/jdbc/DriverManagerDemo.java, Ausschnitt

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

Die Elemente, die durch die Enumeration ausgelesen werden, sind Treiber-Objekte vom Typ Driver. Jeder Datenbanktreiber implementiert diese Schnittstelle, doch da wir bisher keinen Treiber geladen haben und ein normales Programm zunächst keinen Datenbank-treiber anmeldet, ist die Ausgabe der oberen Schleife leer.

Laden wir den JDBC-ODBC-Treiber und den Treiber, der hinter der Klasse com.ibm.db2.jcc.DB2Driver steckt, wie oben, dann besteht die Ausgabe aus der folgenden Zeile:

com.ibm.db2.jcc.DB2Driver
sun.jdbc.odbc.JdbcOdbcDriver

Galileo Computing

20.7.4 Log-Informationen  downtop

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(), die einen PrintWriter als Parameter erwartet. 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 offenbart 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:

Listing 20.7   com/javatutor/insel/jdbc/DriverManagerDemo.java2, Ausschnitt

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. Die Ausgabe zeigt den eingetragenen Treiber.

JdbcOdbcDriver class loaded
DriverManager.initialize: jdbc.drivers = null
JDBC DriverManager initialized
registerDriver: driver[className=sun.jdbc.odbc.JdbcOdbcDriver,sun.jdbc.odbc.
  JdbcOdbcDriver@757aef]
sun.jdbc.odbc.JdbcOdbcDriver

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.5 Verbindung zur Datenbank  toptop

Nach dem Laden des Treibers können wir eine Verbindung zur Datenbank mit Hilfe des Connection-Objekts aufbauen, das DriverManager.getConnection() zurückgibt. Der Methode wird eine Datenbank-URL mitgegeben und optional Benutzername und Passwort.

Die Datenquelle angeben

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

jdbc:Subprotokoll:Datenquellenname

Datenbank Subprotokoll Beispiel
Derby derby:net jdbc:derby:net://localhost:1527/
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 folgende Anweisung verbindet uns mit einer Datenbank, die den Namen »OpenGeoDB« trägt. (Im Fall von ODBC wurde der Name im Datenquellen-Administrator festgelegt und hat nichts mit dem Dateinamen zu tun.)

con = DriverManager.getConnection( "jdbc:derby:net://localhost:1527/OpenGeoDB",
                                   "user",
                                   "pass" );

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 und können auch leere Strings ("") sein, wenn eine Authentifizierung keine Rolle spielt.

Meldet getConnection() keinen Fehler, so liefert sie uns eine geöffnete Datenbankverbindung.


class java.sql.  DriverManager  

gp  static Connection getConnection( String url ) 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.
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, Properties info ) throws SQLException Versucht eine Verbindung zur Datenbank aufzubauen. Im Properties-Objekt können die Felder »user« und »password« sowie weitere Informationen vorhanden sein.

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

Wie der Treiber gefunden wird

Es lohnt sich, einmal hinter die Kulissen der Methode getConnection() zu blicken. 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 der DriverInfo-Objekte abgeht, versucht dieser, sich über die connect()-Methode anzumelden. Bemerkt 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

Da eine Verbindung zu schließen ist (und nicht der DriverManager), finden wir eine Methode close() beim Connection-Objekt. Verbindungen zu schließen ist immens wichtig, sodass dieser Teil im Allgemeinen im finally-Block sitzt.

Connection con = null;
try {
  con = ...
  ...
} finally {
  con.close();       // Umgeben von if con!=null und zusätzlichem try/catch
}

interface java.sql.  Connection  

gp  void close() throws SQLException Schließt die Verbindung zur Datenbank. Auch hier kann eine SQLException auftauchen.

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

 << zurück




Copyright © Galileo Press GmbH 2005
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, Rheinwerkallee 4, 53227 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, info@galileo-press.de