Titel | Inhalt | Suchen | Index | DOC | Handbuch der Java-Programmierung, 5. Auflage |
<< | < | > | >> | API | Kapitel 42 - Datenbankzugriffe mit JDBC |
Bevor mit JDBC auf eine Datenbank zugegriffen werden kann, muss zunächst eine Verbindung zu ihr hergestellt werden. Dazu muss der Datenbanktreiber geladen und initialisiert und mit Hilfe des Treibermanagers ein Verbindungsobjekt beschafft werden. Es bleibt während der gesamten Verbindung bestehen und dient als Lieferant für spezielle Objekte zur Abfrage und Veränderung der Datenbank. Alle Klassen zum Zugriff auf die JDBC-Schnittstelle liegen im Paket java.sql, das am Anfang des Programms importiert werden sollte:
import java.sql.*; |
Jeder JDBC-Treiber hat einen statischen Initialisierer, der beim Laden
der Klasse aufgerufen wird. Seine Aufgabe besteht darin, sich beim
Treibermanager zu registrieren, um
bei späteren Verbindungsanfragen von diesem angesprochen werden
zu können. Das Laden der Treiberklasse wird üblicherweise
durch Aufruf der Methode forName
der Klasse Class
erledigt (siehe Abschnitt 43.2.2).
Um einen Treiber zu laden, muss man also seinen vollständigen
Klassennamen kennen:
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
sun.jdbc.odbc.JdbcOdbcDriver ist der Name der JDBC-ODBC-Bridge, mit der die oben erwähnten Typ-1-Treiber realisiert werden. Die Namen alternativer Treiber sind der Dokumentation des jeweiligen Herstellers zu entnehmen.
Nachdem der Treiber geladen wurde, kann er dazu verwendet werden, eine Verbindung zu einer Datenbank aufzubauen. Dazu wird an die statische Methode getConnection der Klasse DriverManager ein String und eventuell weitere Parameter übergeben, um den Treibertyp, die Datenbank und nötigenfalls weitere Informationen festzulegen. getConnection gibt es in drei Ausprägungen:
static Connection getConnection( String url ) static Connection getConnection( String url, String user, String password ) static Connection getConnection( String url, Properties info ) |
java.sql.DriverManager |
Die erste Variante erwartet lediglich einen Connection-String als
Argument, der in Form eines URL (Uniform Ressource Locator, siehe
Abschnitt 40.1.1) übergeben
wird. Der Connection-String besteht aus mehreren Teilen, die durch
Doppelpunkte voneinander getrennt sind. Der erste Teil ist immer »jdbc«
und zeigt an, dass es sich um einen JDBC-URL handelt. Der zweite Teil
wird als Sub-Protokoll bezeichnet und gibt an, welcher Treiber
verwendet werden soll. Die übrigen Teile sind treiberspezifisch.
Connection-Strings für die JDBC-ODBC-Bridge beginnen immer mit
»jdbc:odbc«, gefolgt von einem weiteren Doppelpunkt, nach
dem der Name der ODBC-Datenquelle angegeben wird:
con = DriverManager.getConnection("jdbc:odbc:DirDB");
Die zweite Variante von getConnection erlaubt es, zusätzlich den Benutzernamen und das Passwort an die Datenbank zu übergeben. Das ist bei vielen Datenbanken erforderlich, um eine Verbindung aufbauen zu können. Bei der dritten Variante können zusätzlich mit Hilfe eines Properties-Objekts weitere, treiberspezifische Informationen übergeben werden. Welche Variante zu verwenden ist, muss der jeweiligen Treiberdokumentation entnommen werden.
Falls die Datenbank nicht geöffnet werden konnte, löst getConnection eine Ausnahme des Typs SQLException aus. Diese Ausnahme wird auch von fast allen anderen Methoden und Klassen verwendet, um einen Fehler beim Zugriff auf die Datenbank anzuzeigen. |
|
Wenn die Verbindung erfolgreich aufgebaut werden konnte, liefert getConnection ein Objekt, das das Interface Connection implementiert. Dieses Verbindungsobjekt repräsentiert die aktuelle Datenbanksitzung und dient dazu, Anweisungsobjekte zu erzeugen und globale Einstellungen an der Datenbank zu verändern. Das Connection-Objekt kann durch Aufruf von close explizit geschlossen werden. Die Verbindung wird automatisch geschlossen, wenn die Connection-Variable vom Garbage Collector zerstört wird.
Alle Abfragen und Änderungen der Datenbank erfolgen mit Hilfe von Anweisungsobjekten. Das sind Objekte, die das Interface Statement oder eines seiner Subinterfaces implementieren und von speziellen Methoden des Connection-Objekts erzeugt werden können:
Statement createStatement() PreparedStatement prepareStatement(String sql) CallableStatement prepareCall(String sql) |
java.sql.Connection |
Die einfachste Form ist dabei das von createStatement erzeugte Statement-Objekt. Es kann dazu verwendet werden, unparametrisierte Abfragen und Änderungen der Datenbank zu erzeugen. Seine beiden wichtigsten Methoden sind executeQuery und executeUpdate. Sie erwarten einen SQL-String als Argument und reichen diesen an die Datenbank weiter. Zurückgegeben wird entweder ein einfacher numerischer Ergebniswert, der den Erfolg der Anweisung anzeigt, oder eine Menge von Datenbanksätzen, die das Ergebnis der Abfrage repräsentieren. Auf die beiden übrigen Anweisungstypen werden wir später zurückkommen.
Statement-Objekte sind bei manchen Treibern kostspielige Ressourcen, deren Erzeugen viel Speicher oder Rechenzeit kostet. Das Erzeugen einer großen Anzahl von Statement-Objekten (beispielsweise beim Durchlaufen einer Schleife) sollte in diesem Fall vermieden werden. Viele JDBC-Programme legen daher nach dem Öffnen der Verbindung eine Reihe von vordefinierten Statement-Objekten an und verwenden diese immer wieder. Obwohl das im Prinzip problemlos möglich ist, kann es in der Praxis leicht dazu führen, dass ein Statement-Objekt, das noch in Gebrauch ist (beispielsweise, weil seine Ergebnismenge noch nicht vollständig abgefragt ist), erneut verwendet wird. Das Verhalten des Programms ist dann natürlich undefiniert. Wir werden später in Abschnitt 42.4.5 eine Lösung für dieses Problem kennenlernen. |
|
Hat man ein Statement-Objekt beschafft, kann dessen Methode executeQuery verwendet werden, um Daten aus der Datenbank zu lesen:
public ResultSet executeQuery(String sql) throws SQLException |
java.sql.Statement |
Die Methode erwartet einen SQL-String in Form einer gültigen
SELECT-Anweisung und gibt ein Objekt
vom Typ ResultSet
zurück, das die Ergebnismenge repräsentiert. Als Argument
dürfen beliebige SELECT-Anweisungen übergeben werden, sofern
sie für die zugrunde liegende Datenbank gültig sind. Die
folgende SQL-Anweisung selektiert beispielsweise alle Sätze aus
der Tabelle dir, deren Feld
did den Wert 7 hat:
SELECT * FROM dir WHERE did = 7
Das zurückgegebene Objekt vom Typ ResultSet besitzt eine Methode next, mit der die Ergebnismenge schrittweise durchlaufen werden kann:
boolean next() |
java.sql.ResultSet |
Nach dem Aufruf von executeQuery steht der Satzzeiger zunächst vor dem ersten Element, jeder Aufruf von next positioniert ihn auf das nächste Element. Der Rückgabewert gibt an, ob die Operation erfolgreich war. Ist er false, gibt es keine weiteren Elemente in der Ergebnismenge. Ist er dagegen true, konnte das nächste Element erfolgreich ausgewählt werden, und mit Hilfe verschiedener get...-Methoden kann nun auf die einzelnen Spalten zugegriffen werden. Jede dieser Methoden steht in zwei unterschiedlichen Varianten zur Verfügung:
Um dem Entwickler lästige Typkonvertierungen zu ersparen, gibt es alle getXXX-Methoden in unterschiedlichen Typisierungen. So liefert beispielsweise getString das gewünschte Feld als String, während getInt es als int zurückgibt. Wo es möglich und sinnvoll ist, werden automatische Typkonvertierungen durchgeführt; getString kann beispielsweise für nahezu alle Typen verwendet werden. Tabelle 42.1 gibt eine Übersicht über die wichtigsten get-Methoden der Klasse ResultSet. In Tabelle 42.4 findet sich eine Übersicht der wichtigsten SQL-Datentypen.
Rückgabewert | Methodenname |
boolean | getBoolean |
byte | getByte |
byte[] | getBytes |
Date | getDate |
double | getDouble |
float | getFloat |
int | getInt |
long | getLong |
short | getShort |
String | getString |
Time | getTime |
Timestamp | getTimestamp |
Tabelle 42.1: get-Methoden von ResultSet
Soll festgestellt werden, ob eine Spalte den Wert NULL hatte, so kann das nach dem Aufruf der get-Methode durch Aufruf von wasNull abgefragt werden. wasNull gibt genau dann true zurück, wenn die letzte abgefragte Spalte einen NULL-Wert als Inhalt hatte. Bei allen Spalten, die NULL-Werte enthalten können, muss diese Abfrage also erfolgen. Bei den get-Methoden, die ein Objekt als Ergebniswert haben, geht es etwas einfacher. Hier wird null zurückgegeben, wenn der Spaltenwert NULL war. |
|
Datenbankänderungen werden mit den SQL-Anweisungen INSERT INTO, UPDATE oder DELETE FROM oder den SQL-DDL-Anweisungen (Data Definition Language) zum Ändern der Datenbankstruktur durchgeführt. Im Gegensatz zu Datenbankabfragen geben diese Anweisungen keine Ergebnismenge zurück, sondern lediglich einen einzelnen Wert. Im Falle von INSERT INTO, UPDATE und DELETE FROM gibt dieser Wert an, wie viele Datensätze von der Änderung betroffen waren, bei DDL-Anweisungen ist er immer 0.
Um solche Anweisungen durchzuführen, stellt das Interface Statement die Methode executeUpdate zur Verfügung:
public int executeUpdate(String sql) throws SQLException |
java.sql.Statement |
Auch sie erwartet als Argument einen String mit einer gültigen
SQL-Anweisung, beispielsweise:
INSERT INTO dir VALUES (1, 'x.txt', 0)
Könnte diese Anweisung erfolgreich ausgeführt werden, würde sie 1 zurückgeben. Andernfalls würde eine SQLException ausgelöst.
Wenn SQL-Anweisungen fehlschlagen, lösen sie normalerweise eine Ausnahme des Typs SQLException aus. Das gilt sowohl, wenn keine Verbindung zur Datenbank zustande gekommen ist, als auch bei allen Arten von Syntaxfehlern in SQL-Anweisungen. Auch bei semantischen Fehlern durch falsche Typisierung oder inhaltlich fehlerhafte SQL-Anweisungen wird eine solche Ausnahme ausgelöst. SQLException ist eine Erweiterung der Klasse Exception und stellt folgende zusätzliche Methoden zur Verfügung:
int getErrorCode() String getSQLState() SQLException getNextException() |
java.sql.SQLException |
Mit getErrorCode kann der herstellerspezifische Fehlercode abgefragt werden, getSQLState liefert den internen SQL-Zustandscode. Etwas ungewöhnlich ist die Methode getNextException, denn sie unterstützt die Verkettung von Ausnahmen. Jeder Aufruf holt die nächste Ausnahme aus der Liste. Ist der Rückgabewert null, gibt es keine weiteren Ausnahmen. Code zum Behandeln einer SQLException könnte also etwa so aussehen:
001 ... 002 catch (SQLException e) { 003 while (e != null) { 004 System.err.println(e.toString()); 005 System.err.println("SQL-State: " + e.getSQLState()); 006 System.err.println("ErrorCode: " + e.getErrorCode()); 007 e = e.getNextException(); 008 } 009 } |
Neben der Klasse SQLException für kritische Ausnahmen und Fehler enthält das Paket java.sql auch die Klasse SQLWarning. Diese wird allerdings nicht geworfen, sondern muss über die Methode getWarnings abgerufen werden. SQLWarning wird von den Klassen Connection, Statement und ResultSet unterstützt.
Die Methode getWarnings liefert Ihnen gegebenenfalls das erste SQLWarning-Objekt zur Auswertung zurück. Sollten weitere Warnungen existieren erhalten Sie diese über die Methode getNextWarning.
Titel | Inhalt | Suchen | Index | DOC | Handbuch der Java-Programmierung, 5. Auflage, Addison Wesley, Version 5.0.2 |
<< | < | > | >> | API | © 1998, 2007 Guido Krüger & Thomas Stark, http://www.javabuch.de |