javaseiten.de   |   Version 0.6
 

6.1. JDBC-Grundlagen

Mit JDBC (Java Database Connectivity) wird eine API der Java-Plattform bezeichnet, die u.a. eine Schnittstelle zu relationalen Datenbanken bereitstellt. Mit JDBC können Verbindungen zu Datenbanken hergestellt werden und Anweisungen mit Hilfe der Datenbanksprache SQL (Structured Query Language) an die Datenbank gerichtet werden. SQL ist eine für relationale Datenbanken weit verbreitete Sprache, die zum großen Teil standardisiert ist. Ein wichtiger Begriff im Zusammenhang mit relationalen Datenbanken ist Tabelle. Eine Tabelle ist ein Satz von Datenelementen (Zellen), die in vertikalen Spalten und horizontalen Zeilen angeordnet sind. Dabei ist die Anzahl der Spalten festgelegt und die Anzahl der Zeilen ist theoretisch nicht beschränkt. Die folgende Darstellung zeigt eine Tabelle mit m Spalten und n Zeilen; die Datenelemente sind mit x bezeichnet.

 S1     S2     ...     Sm
---------------------------
 x11    x12    ...     x1m
 x21    x22    ...     x2m
 .
 xn1    xn2    ...     xnm

Die SQL-Anweisungen

CREATE TABLE MyTable (name VARCHAR(30), khm  SMALLINT)
INSERT INTO MyTable VALUES ('Hans im Glueck', 83)

könnten zur folgenden Tabellendarstellung führen:

 name                  khm
---------------------------
 'Hans im Glueck'      83

Wichtige JDBC-Klassen sind im Paket java.sql enthalten. Sie sollen in den nächsten Abschnitten näher erläutert werden. Dabei wird das Datenbank-Managementsystem (DBMS) Apache-Derby linkextern.gif verwendet. Derby (Java DB) ist ein Open-Source-DBMS, das als Teil des Apache-DB-Projekts entwickelt wird. Als Downloadmöglichkeit wird u.a. das Archiv db-derby-xx-bin.zip angeboten. Nach dem Entpacken des ZIP-Archivs befinden sich wichtige JAR-Archive im Unterverzeichnis lib/. Innerhalb von derby.jar befindet sich z.B. auch die JDBC-Treiberklasse EmbeddedDriver, die für die Herstellung einer Verbindung zu Derby notwendig ist.

Versionen (JDK 6): Die freie Datenbank Apache Derby wurde in JDK 6 mit dem Namen "Java DB" aufgenommen. Derby basiert urspünglich auf der Java-Datenbank Cloudscape, wobei Cloudscape von IBM im Oktober 2004 an die Apache Software Foundation übergeben wurde (Java DB alias Derby alias Cloudscape).

6.1.1. Laden der JDBC-Treiberklasse

Die statische Methode forName der Klasse Class wird benutzt, um die JDBC-Treiberklasse zu laden. Die nächste Anweisung lädt z.B. die Treiberklasse EmbeddedDriver des Apache-Derby-DBMS:

Class.forName("org.apache.derby.jdbc.EmbeddedDriver");

Es stehen folgende Methoden beim Arbeiten mit Datenbanktreibern zur Verfügung:

java.sql 
Class DriverManager 

Methode:
  static void registerDriver(Driver driver)
  static void deregisterDriver(Driver driver)

  static Enumeration<Driver> getDrivers()
  static Driver getDriver(String url)

Die erste Methode registriert den Datenbanktreiber. Eine neu geladene Treiberklasse sollte die Methode registerDriver aufrufen, um sich selbst beim DriverManager zu registrieren. Zu diesem Zweck hat eine JDBC-Treiberklasse einen statischen Initialisierer. Dieser wird beim Laden der Klasse aufgerufen und die darin enthaltenen Anweisungen ausgeführt. Ein derartiger static-Block könnte wie folgt aussehen:

public class NameOfDriver implements java.sql.Driver {

  static {
    try {
      java.sql.DriverManager.registerDriver(new NameOfDriver());
    } catch (SQLException e) {
      System.out.println(e.toString());
    }
  }
...
}

Das Registrieren des Treibers beim Treibermanager erfolgt daher automatisch beim Laden der Treiberklasse mittels Class.forName. Mit Hilfe der Methode deregisterDriver wird der als Argument angegebene Treiber aus der Liste des Treibermanagers entfernt. Der Aufruf der Methode getDrivers gibt eine Aufzählung aller aktuell geladener JDBC-Treiber zurück. Die Methode getDriver erwartet als Argument einen Datenbank-URL-String. Der Aufruf versucht einen geeigneten Treiber aus der Liste des Treibermanagers zu ermitteln, der die angegebene URL handhaben kann. Eine Datenbank-URL hat folgenden Aufbau: jdbc:subprotocol:subname. Der Name der registrierten Apache-Derby-Treiberklasse könnte z.B. erhalten werden, indem jdbc:derby: als URL angegeben wird:

Driver d = DriverManager.getDriver("jdbc:derby:");
System.out.println(d.getClass().getName());

6.1.2. Verbindung zur Datenbank herstellen

Nachdem ein bestimmter JDBC-Treiber für eine dazugehöriges DBMS geladen wurde, kann eine Verbindung zu einer Datenbank hergestellt werden. Zu diesem Zweck werden folgende Methoden bereitgestellt:

java.sql 
Class DriverManager 

Methode:
  static Connection getConnection(String url)
  static Connection getConnection(String url, Properties info)
  static Connection getConnection(String url, String user, String password)

Bei allen drei Methoden ist eine Datenbank-URL erforderlich. Diese URL beginnt mit jdbc:. Der nachfolgende Aufbau ist Abhängig vom Anbieter (Vendor) des konkret eingesetzten DBMS (Treiber). Wird z.B. Apache-Derby als DBMS und als Treiber EmbeddedDriver verwendet, lautet das Format der Datenbank-URL zum Verbindungsaufbau mit dem DBMS:

jdbc:derby:databaseName;URLAttributes

Es kann das optionale URL-Attribut create=true angegeben werden, falls eine Verbindung zur Datenbank mit dem Namen databaseName hergestellte werden soll und diese noch nicht vorhanden ist (bei einer Apache-Derby-Datenbank werden verschiedene Verzeichnisse und Dateien erstellt). Ist zur Herstellung der Datenbankverbindung eine User-Passwort-Kombination nötig, kann jeweils der Datenbanknutzer, für dessen Namen die Verbindung hergestellt werden soll, und dessen Passwort als Paramter übergeben werden. Die Übergabe einer nötigen Kombination aus Nutzer und Passwort kann auch mittels eines java.util.Properties-Objekts geschehen. Es können auch URL-Attribute des Derby-DBMS als Key-Value-Paare aufgenommen werden. Das folgende kurze Code-Fragment versucht eine Verbindung mit der Derby-Datenbank mit dem Namen derbyDB herzustellen. Falls derbyDB noch nicht existiert, wird diese angelegt. Eine Nutzerkennung und ein Passwort werden als zweites und drittes Key-Value-Paar aufgenommen.

Properties info = new Properties();
info.put("create", "true");
info.put("user", "user1");
info.put("password", "password1");
  
Connection con = DriverManager.getConnection("jdbc:derby:derbyDB", info);

6.1.3. SQL-Anweisungen an die Datenbank

Um SQL-Anweisungen von einem DBMS ausführen zu lassen, wird zunächst ein Statement-Objekt beschafft, welches von einem Connection-Objekt erhalten werden kann:

java.sql 
Interface Connection 

Methode:
  Statement createStatement()

Nachdem ein Ausführungsobjekt von einer Datenbankverbindung erzeugt wurde, stehen folgende Methoden zur Verfügung:

java.sql 
Interface Statement 

Methode:
  int executeUpdate(String sql)
  ResultSet executeQuery(String sql)

Die Methode execudeUpdate wird verwendet, wenn SQL-Anweisung wie z.B. INSERT INTO, UPDATE oder DELETE benutzt werden. Derartige Anweisungen werden auch als DML-Anweisungen bezeichnet, wobei DML für "Data Manipulation Language" steht. Die Methode wird auch bei Anweisungen aufgerufen, die keinen Rückgabewert haben (DDL-Anweisungen). Die Abkürzung DDL steht dabei für "Data Definition Laguage". Eine Anweisung, die eine Tabelle innerhalb einer Datenbank anlegt gehört beispielsweise zu den DDL-Anweisungen; eine derartige SQL-Anweisung könnte wie folgt lauten:

CREATE TABLE MyTable (
    name VARCHAR(30),
    khm  SMALLINT
)

Dabei hat die Tabelle den Namen MyTable und besitzt zwei Spalten mit den Namen name und khm. Hinter den Spaltennamen steht der jeweilige SQL-Datentyp, mit dem die einzelnen Tabellenspalten gefüllt werden können. Eine Umsetzung dieser SQL-Anweisung innerhalb einer Java-Applikation ergibt sich dann ausgehend von einem Statement-Objekt zu:

Statement stmt = con.createStatement();
stmt.executeUpdate("CREATE TABLE MyTable ("  +
                       "name VARCHAR(50), "  +
                       "khm  INTEGER"       +
                   ")");

Als nächstes soll in die neu angelegte Tabelle ein Eintrag vorgenommen werden, indem ebenfalls die Methode executeUpdate mit einer entsprechenden SQL-Anweisung verwendet wird:

stmt.executeUpdate("INSERT INTO MyTable VALUES (" +
                       "'Hans im Glueck', "       +
                       "83"                       +
                   ")");

Die Datenbankanweisung fügt eine neue Zeile von Daten ein, mit dem Eintrag 'Hans im Glueck' in die Spalte name und 83 in die Spalte khm (khm steht dabei im Beispiel für die Sammlungsnummer "Kinder- und Hausmärchen" der Gebrüder Grimm).

Mit der Methode executeQuery können einzelne Einträge aus einer Tabelle ausgelesen werden. Der Aufruf dieser Methode gibt als Rückgabewert ein Objekt vom Typ ResultSet zurück. Das Interface ResultSet stellt sogenannte "Getter-Methoden" (getString, getInt, ...) bereit, um Spaltenwerte aus der aktuellen Tabellenzeile abzurufen.

java.sql 
Interface ResultSet 

Methode:
  String getString(int columnIndex) 
  String getString(String columnName) 

  int getInt(int columnIndex) 
  int getInt(String columnName) 

  boolean next()

Einzelne Werte können abgerufen werden, indem entweder die Spaltennummer oder der Spaltenname angegeben wird. Die Nummerierung der Tabellenspalten beginnt mit dem Wert 1. Beim Aufruf von "Getter-Methoden" versucht der JDBC-Treiber SQL-Datentypen (wie z.B. VARCHAR oder INTEGER) in den durch den Namen der "Getter-Methode" angegebenen Java-Typ umzuwandeln und einen geeigneten Wert zurückzugeben. Die JDBC-Spezifikation gibt an, welche Umwandlungen zwischen SQL-Datentypen und Java-Datentypen durch die "Getter-Methoden" erlaubt sind. Die Methode getString kann z.B. bei folgenden SQL-Typen verwendet werden: SMALLINT, INTEGER, FLOAT, CHAR, VARCHAR. Der folgende kurze Programmausschnitt gibt die Inhalte der ersten Zeile der Beispieltabelle aus:

ResultSet rs = stmt.executeQuery("SELECT * FROM MyTable");
rs.next();
System.out.print(rs.getString(1) + ", ");
System.out.println(rs.getInt(2));

Das ResultSet-Objekt hält einen sogenannten Cursor bereit, der auf die akutelle Datenzeile des Ergebnissatzes zeigt. Zu Beginn ist der Cursor vor der ersten Zeile positioniert. Die Methode next bewegt den Cursur zur nächsten Zeile und gibt gegebenenfalls false zurück falls diese nicht existiert. Dieser boolean-Rückgabewert kann genutzt werden, um mit Hilfe einer while-Schleife auf alle vorhanden Tabellenzeilen zuzugreifen.

Falls eine Tabelle innerhalb einer Datenbank nicht mehr benötigt wird kann die SQL-Anweisung DROP TABLE wie folgt verwendet werden, um die Tabelle zu löschen:

stmt.executeUpdate("DROP TABLE MyTable");

6.1.4. Die Klassen SQLException und SQLWarning

Viele in diesem Abschnitt vorgestellte Methoden von Klassen im Paket java.sql verursachen bei Fehlern eine SQLException, die mit einer try-catch-Anweisung behandelt werden kann. Eine SQLException liefert Informationen über Fehler, die beim Arbeiten mit Datenbanken auftreten können und stellt dafür die folgenden Methoden bereit:

java.sql 
Class SQLException 

Methode:
  int getErrorCode()
  String getSQLState()
  SQLException getNextException()

Die Methode getErrorCode ruft den abhnängig vom Anbieter des DBMS generierten Fehlercode ab und mit getSQLState kann der SQL-Status für die Ausnahme abgefragt werden. Durch den Aufruf von getNextException wird die nächste Ausnahme innerhalb der SQLException-Kette abgerufen, falls weitere Ausnahmen für dieses SQLException-Objekt vorhanden sind. Mehrere Ausnahmen können erzeugt werden, um zusätzliche Fehlerinformationen bereitzustellen. Eine Behandlung von SQL-Ausnahmen könnte wie folgt aussehen:

try {
...
// Mehodenaufrufe die eine SQLException ausloesen
...
} catch (SQLException e) {
  while (e != null) {
    System.out.println(e.toString());
    System.out.println("ErrorCode: " + e.getErrorCode());
    System.out.println("SQLState: " + e.getSQLState());
    e = e.getNextException();
  }
}

Liegt z.B. ein syntaktischer Fehler innerhalb der SQL-Anweisung vor erscheint folgende Fehlermeldung (bei Syntaxfehlern beginnt die SQL-Statusnummer mit einer 42):

SQL Exception: Syntax error: ...
ErrorCode: 30000
SQLState: 42X01

SQL-Satusnummern werden zu Blöcken zusammnengefasst, die mit denselben Zahlen bzw. Buchstaben beginnen. Statusnummern, die mit ein X beginnen sind speziell für das DBMS Derby. Einige SQLState-Blöcke sind:

04...  Datenbank-Authentifikations-Fehler (z.B. 04501  Database connection refused)
08...  Verbindungsfehler (z.B. 08001  No suitable driver)
22...  Datenfehler (z.B. 22003  The resulting value is outside the range for the 
           data type <datatypeName>)
42...  Syntaxfehler (z.B. 42X01  Syntax error: <error>)
X0X..  Ausfühungsfehler (z.B. X0X05  Table '<tableName>' does not exist)
XCY..  Property-Fehler (z.B. XCY03  Required property '<propertyName>' 
           has not been set)
.....

Neben SQL-Ausnahmen können auch SQL-Warnungen erzeugt werden. Warnungen können von Connection-, Statement- und ResultSet-Objekten erhalten werden.

java.sql 
Interface Connection 

Methode:
  SQLWarning getWarnings()

Der Aufruf der Methode getWarnings gibt das erste SQLWarning-Objekt zurück. Gibt es keine Warnungen wird null zurückgegeben. Falls mehrere Warnungen erzeugt wurden, können diese durch Aufruf der Methode getNextWarning der SQLWarning-Klasse abgerufen werden, die SQLException erweitert.

java.sql 
Class SQLWarning 

Methode:
  SQLWarning getNextWarning()

SQL-Warnungen können in ähnlicher Weise wie SQL-Ausnahmen behandelt werden:

Connection con = DriverManager.getConnection("jdbc:derby:derbyDB;create=true");
SQLWarning w = con.getWarnings(); 
  while (w != null) {
    System.out.println(w.toString());
    System.out.println("ErrorCode: " + w.getErrorCode());
    System.out.println("SQLState: " + w.getSQLState());
    w = w.getNextWarning();
  }

Durch Angabe des URL-Attributs create=true soll eine neue Datenbank mit dem Namen derbyDB erstellt werden, falls diese noch nicht vorhanden ist. Für den Fall, dass die Datenbank bereits angelegt wurde wird die folgende Warnung generiert:

SQL Warning: Database 'derbyDB' not created, connection made to existing
database instead.
ErrorCode: 10000
SQLState: 01J01

6.1.5. Beispiel

Das folgende kurze Listing erzeugt zunächst eine Derby-Datenbank und stellt eine Verbindung zu dieser her. Anschließend wird eine Tabelle mit zwei Spalten angelegt und ein Eintrag vorgenommen. Der Eintrag wird danach mit einer SQL-Anweisung wieder ausgelesen und in der Konsole ausgegeben. Schließlich wird die verwendete Tabelle wieder gelöscht. Beim Erstellen einer neuen Derby-Datenbank wird im aktuellen Verzeichnis ein neuer Ordner mit dem Namen der Datenbank erstellt, in dem sich weitere Verzeichnisse und Dateien befinden. Ein Verzeichnis lautet z.B. seg0, in dem u.a. Dateien für die Daten von Tabellen stehen. Damit die benötigte Derby-Treiberklasse EmbeddedDriver gefunden wird, muss das Archiv derby.jar in den Klassenpfad aufgenommen werden. Dies kann z.B. geschehen, indem die Option -classpath des Java-Anwendungs-Starters java genutzt wird (das JAR-Archiv befindet sich z.B. im Verzeichnis c:/db-derby/lib/ und der Punkt im Klassenpfad steht für das aktuelle Verzeichnis):

java -classpath .;c:/db-derby/lib/derby.jar JDBCExample

Listing 6.1. JDBCExample.java

/* 
 * JDBCExample.java
 *
 */

import java.sql.*;

public class JDBCExample {
    
  public static void main(String[] args) {
    
    try {
      Class.forName("org.apache.derby.jdbc.EmbeddedDriver");
    } catch (ClassNotFoundException e) {
      System.out.println(e.toString());
      System.exit(1);
    }
    
    try {
      Connection con = DriverManager.getConnection("jdbc:derby:derbyDB;create=true");
                     
      Statement stmt = con.createStatement();
      String strCreateMyTable = 
          "CREATE TABLE MyTable (" +
              "name VARCHAR(50), "     +
              "khm  INTEGER"       +
          ")";
      stmt.executeUpdate(strCreateMyTable);
      String strInsertIntoMyTable =
          "INSERT INTO MyTable VALUES (" +
              "'Hans im Glueck', "       +
              "83"                       +
          ")";    
      stmt.executeUpdate(strInsertIntoMyTable); 
      
      ResultSet rs = stmt.executeQuery("SELECT * FROM MyTable");
      rs.next();
      System.out.print(rs.getString(1) + ", ");
      System.out.println(rs.getInt(2));
            
      stmt.executeUpdate("DROP TABLE MyTable");             
      
      rs.close();
      stmt.close();
      con.close();
    } catch (SQLException e) {
      while (e != null) {
        System.out.println(e.toString());
        System.out.println("ErrorCode: " + e.getErrorCode());
        System.out.println("SQLState: " + e.getSQLState());
        e = e.getNextException();
      }
    } 
    
  }  
}

 

 

 

Diese Seite nutzt Google-Dienste - siehe dazu Datenschutz.

Copyright © 2006, 2007 Harald Roeder