Inhalt

2. Zugriff auf die Datenbank mit JDBC

2.1. SQL-Befehle

Wir haben uns entschieden, alle für die Darstellung der virtuellen 3D-Gebäude notwendigen Informationen in einer Datenbank zu speichern. Als Datenbanksoftware wählten wir MySQL. Außer der kostenlosen Verfügbarkeit unter Linux waren folgende Punkte für uns von Bedeutung: MySQL ist eine echte Multi-User, Multi-Threaded SQL Datenbank und wird von allen großen Providern oder auch Suchmaschinenbetreibern eingesetzt. MySQL ist eine CLient/Server Implementierung, die aus einem Server-Dämon mysqld und vielen Client Programmen besteht. MySQL ist äußerst schnell und flexibel genug, um sogar Bilder und Log-Dateien darin abzulegen. In der Praxis ist MySQL sehr viel schneller, als z.B. ORACLE oder INFORMIX.

Wie der Name sicher schon nahe legt, werden Anweisungen an die Datenbank mit Hilfe der Sprache SQL formuliert. SQL ist eine standardisierte Datenbanksprache, die das Speichern, Updaten und den Zugriff auf Informationen erleichtert. SQL steht für Structured Query Language und wurde Ende der 70'er Jahre bei IBM in San Jose, Kalifornien als Abfragesprache für die relationale Datenbank DB2 entworfen. Es handelte sich hier ursprünglich um eine nichtprozedurale Sprache, die also keine Schleifen, Unterprogramme, Funktionen und Funktionsübergabeparameter u.s.w. enthält.

SQL ist also in dem Sinne keine Programmiersprache und dementsprechend einfach zu erlernen. Die SQL Befehle setzen sich aus zwei Teilen zusammen, der Data Definition Language (DDL) und der Data Manipulation Laguage (DML). Die DDL dient dem Aufsetzen der Datenbankstruktur, die DML dient der Manipulation der darin enthaltenen Daten.

Die wichtigsten von uns verwendeten SQL-Befehle wollen wir kurz erklären.

2.1.1 Tabellen erstellen

CREATE TABLE erstellt eine neue Tabelle in der aktuellen Datenbank. tbl_name gibt den Namen der neuen Tabelle an. Unter create_definition müssen Name und Typ für jede Spalte angegeben werden. Die Spalten werden durch Kommata voneinader getrennt. Der PRIMARY KEY dient als Index und ist eindeutig für jede Tabelle.
    CREATE TABLE tbl_name (create_definition,...)
    
In unserem Beispiel wird eine neue Tabelle room angelegt. Hier sollen alle relevanten Informationen, die wir zum Erstellen eines Raumes benötigen, gespeichert werden. Wir haben uns im Beispiel der Einfachheit halber nur auf die eindeutige ID (Index), dem Namen des Raumes und dessen Ursprungskoordinaten beschränkt. Natürlich werden keine Tabellen durch unser 3D-Simulationsprogramm erzeugt oder verändert. Diese Aufgabe bleibt dem Betreuer der Datenbank überlassen.
    CREATE TABLE room (ID INTEGER PRIMARY KEY,
                       Name VARCHAR(30),
                       Origin_x DOUBLE,
                       Origin_y DOUBLE,
                       Origin_z DOUBLE)
    

2.1.2 Tabellen löschen

DROP TABLE entfernt eine oder mehrere Tabellen sowie deren Definitionen. tbl_name gibt wiederum den Namen der zu löschenden Tabelle an.
    DROP TABLE tbl_name
    
In unserem Beispiel wird die Tabelle room gelöscht. Das Entfernen von Tabellen wird wiederum nicht vom 3D-Simulationsprogramm durchgeführt und ist nur dem Betreuer der Datenbank erlaubt.
    DROP TABLE room
    

2.1.3 Neue Zeile einfügen

INSERT fügt neue Reihen in eine Tabelle ein. Der INSERT ... VALUES Ausdruck fügt Reihen ein, unter Verwendung genau angegeber Zahlen. tbl_name ist die Tabelle, in welche die Reihen eingefügt werden sollen.
    INSERT INTO tbl_name VALUES (expression,...)
    
In unserem Beispiel wird in die Tabelle room der Raum mit dem Namen FR0001 und den Ursprungskoordinaten 1, 13.75 und 25.2 eingefügt.
    INSERT INTO room VALUES (3, 'FR0001', 1, 13.75, 25.2)
    

2.1.4 Zeile zurückliefern

SELECT wird eingesetzt, um Reihen aus einer oder mehreren Tabellen abzufragen. Somit ist es möglich, an in der Tabelle gespeicherte Informationen zu gelangen und damit zu arbeiten. select_expression zeigt an, welche Spalten ausgewählt werden sollen. Alle Schlüsslworte müssen in exakt dieser Reihenfolge angegeben werden. Die FROM table-references Ausdrücke zeigen an, daß Informationen aus den entsprechenden Tabellen selektiert werden. Mit Hilfe von where_definition werden die relevanten Zeilen ausgewählt.
    SELECT select_expression,... FROM table_references WHERE where_definition
    
In unserem Beispiel wollen wir Name und Ursprungskoordinaten des Raumes in der Tabelle room ermitteln, der die ID 3 hat.
    SELECT Name, Origin_x, Origin_y, Origin_z FROM room WHERE ID = 3
    

2.1.5 Zeile aktualisieren

UPDATE setzt neue Werte in Spalten von existierenden Tabellen ein. Der Ausdruck SET zeigt an, welche Spalten modifiziert werden sollen. Die Option WHERE, gibt die Reihe an, andernfalls werden alle Reihen modifiziert.
    UPDATE tbl_name SET col_name1=expr1,col_name2=expr2,... WHERE where_definition
    
Das folgende Beispiel zeigt, wie der Raumname in der Tabelle room mit der ID 3 auf FR3056 gesetzt wird.
    UPDATE room SET Name = 'FR3056' WHERE ID = 3
    

2.1.6 Zeile löschen

DELETE löscht von tbl_name diejenigen Reihen, auf welche die Bedingung where_definition zutrifft, und gibt die Zahl der gelöschten Einträge zurück. Um alle Reihen zu löschen gibt man keine Bedingung mit an.
    DELETE FROM tbl_name WHERE where_definition
    
Hier wird in der Tabelle room die Spalte mit der ID 3 gelöscht.
    DELETE FROM room WHERE ID = 3
    

2.2. Datenbankanbindung mit JDBC

Nun ist es wichtig, unseren Applikationen, die mit Java3D erstellt wurden, eine Möglichkeit zu geben, auf die Datenbank zuzugreifen. Für die Datenbankanbindung bietet sich JDBC (Java Database Connectivity) an, das eine universelle Schnittstelle zwischen einer Vielzahl von Datenbanken (so auch MySQL) und Java bereitstellt.

Die Datenbankanbindung mit JDBC kann man ganz allgemein in die folgenden vier Schritte unterteilen.

2.2.1 JDBC-Treiber auswählen

Die Programmierung von JDBC an sich ist recht einfach. Nur wenige Zeilen Programmcode genügen, um eine Datenbankverbindung aufzubauen und einen SQL-Befehl abzusetzen. Der kompliziertere Teil ist die Vorarbeit und besteht darin, einen geeigneten Treiber auszuwählen.

Die Architektur von JDBC ähnelt sehr der von ODBC: Die zentrale Rolle spielen die angesprochenden JDBC-Treiber, die ein Treibermanager verwaltet. Es gibt vier verschiedene Typen von Treibern:

2.2.1.1 Typ 1

Eine JDBC-ODBC-Bridge verwendet einen auf dem Client installierten spezifischen ODBC-Treiber der eingesetzten Datenbank. Diese Lösung ist für den Einsatz im Internet eher ungeeignet, da sie die Installation des Treibers auf dem Client voraussetzt, bevor das Applet benutzt werden kann.

JDBC-Treiber: Typ1

2.2.1.2 Typ 2

Native API-Treiber verwenden Java, um für die Verbindung zur Datenbank Funktionen eines Zugriffs-API aufzurufen. Diese Treiber sind also ebenfalls auf vorinstallierter Software auf Seite der Clients angewiesen.

JDBC-Treiber: Typ2

2.2.1.3. Typ 3

Native Protokolltreiber verwenden das Netzwerkprotokoll des Datenbankmangementsystems, um eine direkte Verbindung zur Datenbank einzurichten. Verfügt die eingesetzte Datenbank über eine JDBC-Schnittstelle, werden sie in der Regel vom Datenbankhersteller mitgeliefert. Dies ist natürlich eine sehr effiziente Lösung für den Einsatz im Internet, da diese Java-Treiber übers Netz nachgeladen werden können und somit keine zusätzliche Software auf dem Client erforderlich ist. Allerdings verlangen die Sicherheitbestimmungen von Java, dass in diesem Falle die Datenbank auf demselben Rechner zu erreichen ist, wie der Web-Server, von dem das Applet stammt.

JDBC-Treiber: Typ3

2.2.1.4 Typ 4

JDBC-Netztreiber verwenden dagegen die Netzwerkprotokolle des JDK, um eine Verbindung zu einer Serverkomponente auf dem Web-Server herzustellen. Diese können Sie entweder selbst entwickeln oder aber kommerzielle Middleware-Produkte einsetzen. Die Serverkomponete übersetzt Befehle in datenbankspezifische Anfragen und schickt diese an die Datenbank. Auch hierfür muss auf dem Client nichts vorinstalliert sein. Weiterhin hat diese Lösung den Vorteil, dass nur die Serverkomponente, nicht aber die Datenbank auf demselben Rechner wie der Web-Server laufen muss.

JDBC-Treiber: Typ4

Hat man sich für einen Treiber entschieden, kann es richtig losgehen. Nun muss der ausgewählte JDBC-Treiber geladen werden, was zum Beispiel die folgende Zeile bewirkt:

    Class.forName("org.gjt.mm.mysql.Driver");
    
Dieser Aufruf macht der Java-Umgebung Driver bekannt. Für den Fall, dass der Aufruf fehlschlägt, sollte die Anweisung in einem Try/Catch-Block stehen, der Ausnahmesituationen behandelt. Wenn sich der Treiber nicht auf dem Client befindet, versucht der Browser die benötigten Klassen übers Web nachzuladen. Erst wenn auch das keinen Erfolg hat, tritt eine Exception auf.

2.2.2 Verbindung zur Datenbank herstellen

Als Nächstes muss das Programm eine Verbindung zur Datenbank herstellen. Dies geschieht am einfachsten durch den Aufruf der Methode DriverManager.getConnection(). Sie erwartet drei Parameter, von denen der zweite und der dritte Benutzername und Passwort spezifizieren. Der Erste enthält eine URL, die folgendermaßen aufgebaut ist:
    jdbc:<Protokoll>:<Datenbank-URL>
    
Der DriverManager sucht dann selbstständig nach einem geeigneten Treiber für das angegebene Protokoll, im Beispiel also 'mysql'. Wurde vorher kein entsprechender Treiber via Class.forName() geladen, erzeugt der Manager eine Exception.

Die Form der Datenbank-URL gibt an, wo und wie die Datenbank zu erreichen ist. Ihre Form hängt vom verwendeten Treiber ab. In unserem Fall hat die URL folgende Form:

    String url = "jdbc:mysql://lava.cs.tu-berlin.de/franklin";
    
Der Aufruf von
    Connection con = DriverManager.getConnection
                     (url, "username", "password");
    
erzeugt dann eine Verbindung zur Datenbank, über die das Programm SQL-Abfragen absetzen kann.

2.2.3 SQL-Anweisung an die Datenbank schicken

Nun können beliebige SQL-Anweisungen zu einem String zusammengesetzt werden und an die Datenbank geschickt werden.
    String stringSelect = "SELECT ID FROM wall WHERE RoomID = " + roomID;
    
Auch Datenbank-spezifische Erweiterungen oder Nicht-SQL-Anweisungen können an die Datenbank gesendet werden. Es liegt aber in der Verantwortung des Entwicklers, sicherzustellen, dass die Datenbank auf der anderen Seite der Verbindung damit etwas anfangen kann, beziehungsweise eventuelle Fehler abzufangen.

Um einfache SQL-Aktionen durchführen zu können, muss für die Verbindung jeweils ein Statement-Objekt erzeugt werden.

    Statement stmtSelect = con.createStatement();
    
Für komplexere Anwendungen gibt es die Methoden prepareStatement() und prepareCall(). Da wir diese Methoden nicht verwenden, gehen wir hier nicht weiter darauf ein.

Das Statement-Object stellt die Methoden executeQuery() und executeUpdate() bereit. Über executeQuery() lassen sich SQL-Abfragen absetzen, die eine Menge von Datensätzen, also ein ResultSet, zurückliefern.

    ResultSet rs = stmtSelect.executeQuery(stringSelect);
    
Auf die einzelnen Datensätze kann man anschließend etwa innerhalb einer Schleife mit rs.next() zugreifen. Sie bewegt den Datenbank-Cursor auf die nächste Tabellenzeile.
    while (rs.next()) {
	      wall_IDs[i] = rs.getInt("ID");
	      i++;
    }
    
Außerdem enthält ein ResultSet-Objekt get-Methoden für alle SQL-Datentypen, die ein entsprechendes Java-Objekt zurückliefern. Die Methoden sind nach den Java-Objekten benannt, die man durch ihren Aufruf erhält. So liefert beispielsweise rs.getInt() ein Int-Objekt zurück.

executeUpdate() dient der Modifikation von Tabellen und kann einfache SQL-Anweisungen wie INSERT, UPDATE, DELETE, aber auch CREATE TABLE oder DROP TABLE ausführen, die sich auf die gesamte Tabelle beziehen. Die Anweisung

    String stringDelete = "DELETE FROM wall WHERE ID=" + id;
    Statement stmtDelete = con.createStatement();
    int rowCount = stmtDelete.executeUpdate(stringDelete);
    
löscht die Zeile mit dem id-Feld 'id' aus der Tabelle wall. rowCount enthält die Zahl der betroffenen Zeilen.

2.2.4 Aufgebaute Verbindung schließen

Nach jeder abgeschlossenen SQL-Aktion sollte man die Verbindung mit
    con.close();
    
explizit schließen und so die dafür reservierten Ressourcen freigeben.

2.3. Kapselung der Datenbank

Die Kapselung der Datenbank enthält Java-Methoden, um Informationen in der Datenbank zu speichern bzw. aus der Datenbank zu laden. Dies ermöglicht die Arbeit in der Browser- und Editorgruppe, ohne SQL-Kenntnisse anzuwenden bzw. erwerben zu müssen. Die Struktur der Datenbank kann außerdem ausgetauscht werden, ohne dass Änderungen am Editor bzw. Browser vorgenommen werden müssen. Lediglich die Kapselung muss angepasst werden. Es erfolgt eine Fehlerminimierung, da keine fehlerhaften SQL-Anweisungen an die Datenbank geschickt werden können. Alle notwendigen SQL-Befehle sind in den Methoden der Kapselung enthalten.

Kapselung der Datenbank

Die genaue Struktur der Kapselung findet man in der mit javadoc erstellten Dokumentation.